├── docs
├── .vitepress
│ ├── cache
│ │ └── deps
│ │ │ ├── package.json
│ │ │ ├── vue.js.map
│ │ │ ├── vitepress___@vueuse_core.js.map
│ │ │ ├── _metadata.json
│ │ │ ├── vue.js
│ │ │ └── vitepress___@vueuse_core.js
│ ├── theme
│ │ ├── index.ts
│ │ └── style.less
│ └── config.ts
├── changelog.md
├── guide
│ ├── introduction.md
│ ├── quick-start.md
│ ├── install.md
│ └── modules
│ │ ├── color.md
│ │ ├── speech.md
│ │ ├── data.md
│ │ ├── string.md
│ │ ├── event.md
│ │ ├── file.md
│ │ ├── number.md
│ │ ├── platform.md
│ │ ├── common.md
│ │ └── element.md
└── index.md
├── README.md
├── examples
├── main.ts
└── App.vue
├── .npmignore
├── tsconfig.node.json
├── .gitignore
├── index.html
├── lib
├── color.d.ts
├── number.d.ts
├── event.d.ts
├── string.d.ts
├── speech.d.ts
├── data.d.ts
├── file.d.ts
├── common.d.ts
├── platform.d.ts
├── element.d.ts
├── index.d.ts
├── dap-util.umd.js
└── dap-util.es.js
├── tsconfig.json
├── src
├── index.ts
├── data.ts
├── string.ts
├── number.ts
├── speech.ts
├── color.ts
├── file.ts
├── event.ts
├── platform.ts
├── common.ts
└── element.ts
├── vite.config.ts
├── package.json
└── LICENSE
/docs/.vitepress/cache/deps/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #### 一个提供大量便捷高效方法的 JS 库
2 |
3 | > 具体使用方法请参阅:[dap-util](https://www.so-better.cn/docs/dap-util/)
4 |
--------------------------------------------------------------------------------
/examples/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 |
4 | createApp(App).mount('#app')
5 |
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vue.js.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "sources": [],
4 | "sourcesContent": [],
5 | "mappings": "",
6 | "names": []
7 | }
8 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | import DefaultTheme from 'vitepress/theme'
2 | import './style.less'
3 |
4 | export default {
5 | extends: DefaultTheme
6 | }
7 |
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "sources": [],
4 | "sourcesContent": [],
5 | "mappings": "",
6 | "names": []
7 | }
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # 忽略目录
2 | examples/
3 | public/
4 | docs/
5 | src/
6 |
7 | # 忽略指定文件
8 | LICENSE
9 | index.html
10 | vite.config.ts
11 | tsconfig.json
12 | tsconfig.node.json
13 | package-lock.json
14 | vite-env.d.ts
15 |
16 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/examples/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | ---
2 | lastUpdated: false
3 | title: 更新日志
4 | ---
5 |
6 | # 更新日志
7 |
8 | ## v1.6.0
9 |
10 | - 优化 TS 类型
11 | - 部分方法优化
12 | - 文档完善
13 |
14 | ## v1.5.9
15 |
16 | - 优化 TS 类型
17 | - 修复 matchingText 方法中对十六进制颜色值判断错误的 bug
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | dist
4 | docs/dap-util
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | /package-lock.json
25 |
--------------------------------------------------------------------------------
/docs/guide/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 简介
3 | ---
4 |
5 | # 简介
6 |
7 | `dap-util` 是一个不依赖于任何库的工具性质的 `JS` 库,它提供了大量的工具函数来方便我们的开发
8 |
9 | ## 模块
10 |
11 | `dap-util` 内的工具方法,是按照不同的模块加以归类和使用的,目前的模块有:
12 |
13 | - `color`:颜色模块
14 | - `common`:通用模块
15 | - `data`:数据模块
16 | - `element`:dom 模块
17 | - `event`:事件模块
18 | - `file`:文件模块
19 | - `number`:数字模块
20 | - `platform`:平台终端模块
21 | - `speech`:语音合成模块
22 | - `string`:字符串模块
23 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | dap-util JS工具库
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/lib/color.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 颜色相关方法
3 | */
4 | export declare const color: {
5 | /**
6 | * rgb转hsv值
7 | * @param {Object} rgb rgb值,数组
8 | */
9 | rgb2hsv(rgb: number[]): number[];
10 | /**
11 | * hsv格式值转rgb值
12 | * @param {Object} hsv hsv值,数组
13 | */
14 | hsv2rgb(hsv: number[]): number[];
15 | /**
16 | * rgb值转十六进制
17 | * @param {Array} rgb rgb值,数组
18 | */
19 | rgb2hex(rgb: number[]): string;
20 | /**
21 | * 十六进制颜色转rgb
22 | * @param {String} hex 十六进制颜色值
23 | */
24 | hex2rgb(hex: string): number[];
25 | };
26 |
--------------------------------------------------------------------------------
/lib/number.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 数字相关方法
3 | */
4 | export declare const number: {
5 | /**
6 | * 数字格式化
7 | * @param {Number} num
8 | */
9 | formatNumber(num: number): string;
10 | /**
11 | * 判断是否数字
12 | * @param {Object} num
13 | */
14 | isNumber(num: any): boolean;
15 | /**
16 | * 多个数的加法运算
17 | */
18 | add(...values: number[]): number;
19 | /**
20 | * 多个数的减法运算
21 | */
22 | subtract(...values: number[]): number;
23 | /**
24 | * 多个数的乘法运算
25 | */
26 | mutiply(...values: number[]): number;
27 | /**
28 | * 多个数的除法运算
29 | */
30 | divide(...values: number[]): number;
31 | };
32 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
7 | "skipLibCheck": true,
8 | "allowSyntheticDefaultImports": true,
9 | "types": ["node"],
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "preserve",
18 |
19 | /* Linting */
20 | "strict": true,
21 | "noUnusedLocals": true,
22 | "noUnusedParameters": true,
23 | "noFallthroughCasesInSwitch": true
24 | },
25 | "include": ["src/**/*.ts"],
26 | "references": [{ "path": "./tsconfig.node.json" }]
27 | }
28 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { color } from './color'
2 | import { common } from './common'
3 | import { data } from './data'
4 | import { element } from './element'
5 | import { event } from './event'
6 | import { file } from './file'
7 | import { number } from './number'
8 | import { platform } from './platform'
9 | import { speech } from './speech'
10 | import { string } from './string'
11 |
12 | export * from './color'
13 | export * from './common'
14 | export * from './data'
15 | export * from './element'
16 | export * from './event'
17 | export * from './file'
18 | export * from './number'
19 | export * from './platform'
20 | export * from './speech'
21 | export * from './string'
22 |
23 | export default { number, data, element, event, common, color, file, string, platform, speech }
24 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | import dts from 'vite-plugin-dts'
4 | import path from 'path'
5 |
6 | export default defineConfig({
7 | plugins: [vue(), dts()],
8 | build: {
9 | //打包后的目录名称
10 | outDir: 'lib',
11 | minify: 'terser',
12 | lib: {
13 | entry: path.resolve(__dirname, 'src/index.ts'),
14 | name: 'Dap',
15 | fileName: format => `dap-util.${format}.js`
16 | },
17 | rollupOptions: {
18 | // 确保外部化处理那些你不想打包进库的依赖
19 | external: ['vue'],
20 | output: {
21 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
22 | globals: {
23 | vue: 'Vue'
24 | },
25 | exports: 'named'
26 | }
27 | },
28 | sourcemap: false //是否构建source map 文件
29 | }
30 | })
31 |
--------------------------------------------------------------------------------
/docs/guide/quick-start.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 快速上手
3 | ---
4 |
5 | # 快速上手
6 |
7 | ## 获取 dap-util 提供的对象
8 |
9 | - 如果你是通过 `cdn` 在线引入 `dap-util` 或者下载 `dap-util` 文件到本地使用,直接通过 `window` 获取 `dap-util` 提供的对象
10 |
11 | ```ts
12 | const Dap = window.Dap
13 | ```
14 |
15 | - 如果你是通过 `npm/yarn/pnpm` 引入
16 |
17 | ```ts
18 | import Dap from 'dap-util'
19 | ```
20 |
21 | ## 使用方法
22 |
23 | 获取到 `dap-util` 的 `Dap` 对象后,即可通过 `Dap` 来调用各个模块里的函数,如下:
24 |
25 | ```ts
26 | //调用platform模块的device方法获取运行设备的信息
27 | const result = Dap.platform.device()
28 | ```
29 |
30 | ```ts
31 | //调用common模块的isObeject方法判断是否对象
32 | const isObj = Dap.common.isObject(val)
33 | ```
34 |
35 | > 关于 dap-util 的模块会在后面文档中尽数列出,具体使用请看后续文档
36 |
37 | ## 按需引入
38 |
39 | `dap-util` 支持按需引入的方式,如下:
40 |
41 | ```ts
42 | //按需引入platform模块来使用
43 | import { platform } from 'dap-util'
44 | const result = platform.device()
45 | ```
46 |
47 | ```ts
48 | //按需引入common模块来使用
49 | import { common } from 'dap-util'
50 | const isObj = common.isObject(val)
51 | ```
52 |
--------------------------------------------------------------------------------
/lib/event.d.ts:
--------------------------------------------------------------------------------
1 | export type EventNameObjType = {
2 | eventName?: string;
3 | guid?: string | number;
4 | };
5 | export type EventObjType = {
6 | [key: string]: {
7 | type: string;
8 | fn: EventListenerOrEventListenerObject;
9 | options?: AddEventListenerOptions;
10 | } | undefined;
11 | };
12 | export declare const event: {
13 | /**
14 | * 绑定事件
15 | * @param {Object} el 元素节点
16 | * @param {Object} eventName 事件名称
17 | * @param {Object} fn 函数
18 | * @param {Object} options 参数
19 | */
20 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions): void;
21 | /**
22 | * 事件解绑
23 | * @param {Object} el 元素节点
24 | * @param {Object} eventName 事件名称
25 | */
26 | off(el: HTMLElement | Window | Document, eventName?: string): void;
27 | /**
28 | * 获取绑定的所有事件
29 | * @param {*} el
30 | */
31 | get(el: HTMLElement | Window | Document): EventObjType | undefined;
32 | };
33 |
--------------------------------------------------------------------------------
/lib/string.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 字符串操作
3 | */
4 | export declare const string: {
5 | /**
6 | * 向指定位置插入字符串
7 | * @param {Object} original 原始字符串
8 | * @param {Object} str 插入的字符串
9 | * @param {Object} index 插入的位置
10 | */
11 | insert(original: string, str: string, index: number): string;
12 | /**
13 | * 删除指定位置的字符串
14 | * @param {Object} original 原始字符串
15 | * @param {Object} index 删除的位置序列
16 | * @param {Object} num 删除的字符串长度
17 | */
18 | delete(original: string, index: number, num: number): string;
19 | /**
20 | * 替换指定位置的字符串
21 | * @param {Object} original 原始字符串
22 | * @param {Object} start 开始位置
23 | * @param {Object} end 结束位置
24 | * @param {Object} str 替换的字符串
25 | */
26 | replace(original: string, start: number, end: number, str: string): string;
27 | /**
28 | * 去除字符串空格
29 | * @param {Object} str 原始字符串
30 | * @param {Object} global 为true时去除所有空格,否则只去除两边空格
31 | */
32 | trim(str: string, global?: boolean): string;
33 | };
34 |
--------------------------------------------------------------------------------
/lib/speech.d.ts:
--------------------------------------------------------------------------------
1 | export type EventParamsType = {
2 | text: string;
3 | pitch: number;
4 | rate: number;
5 | volume: number;
6 | };
7 | export type SpeechParamsType = {
8 | pitch?: number;
9 | rate?: number;
10 | volume?: number;
11 | start?: (e: Event, options: EventParamsType) => void;
12 | end?: (e: Event, options: EventParamsType) => void;
13 | pause?: (e: Event, options: EventParamsType) => void;
14 | resume?: (e: Event, options: EventParamsType) => void;
15 | error?: (e: Event, options: EventParamsType) => void;
16 | };
17 | /**
18 | * 语音合成方法
19 | */
20 | export declare const speech: {
21 | /**
22 | * 将文字加入语音播报队列
23 | * @param {Object} text
24 | * @param {Object} options
25 | */
26 | start(text: string, options?: SpeechParamsType): void;
27 | /**
28 | * 停止播报,停止所有播报队列里面的语音
29 | */
30 | stop(): void;
31 | /**
32 | * 暂停播报
33 | */
34 | pause(): void;
35 | /**
36 | * 恢复暂停的播报
37 | */
38 | resume(): void;
39 | };
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dap-util",
3 | "version": "1.6.0",
4 | "private": false,
5 | "type": "module",
6 | "author": "so-better",
7 | "main": "lib/dap-util.umd.js",
8 | "module": "lib/dap-util.es.js",
9 | "types": "lib/index.d.ts",
10 | "license": "MIT",
11 | "description": "一个轻量的前端JavaScript工具库,专注于JavaScript,不关心UI",
12 | "scripts": {
13 | "dev": "vite --host",
14 | "lib": "tsc && vite build",
15 | "docs:dev": "vitepress dev docs",
16 | "docs:build": "vitepress build docs"
17 | },
18 | "dependencies": {
19 | "vue": "^3.3.13"
20 | },
21 | "devDependencies": {
22 | "@types/node": "^20.11.24",
23 | "terser": "^5.16.9",
24 | "vite": "^5.4.10",
25 | "less": "^3.0.4",
26 | "less-loader": "^5.0.0",
27 | "@vitejs/plugin-vue": "^5.0.4",
28 | "typescript": "^5.6.3",
29 | "vite-plugin-dts": "^4.3.0",
30 | "vitepress": "^1.5.0"
31 | },
32 | "browserslist": [
33 | "> 1%",
34 | "last 2 versions",
35 | "not dead"
36 | ],
37 | "repository": {
38 | "type": "git",
39 | "url": "https://github.com/so-better/dap-util"
40 | }
41 | }
--------------------------------------------------------------------------------
/lib/data.d.ts:
--------------------------------------------------------------------------------
1 | export type DataHTMLElement = HTMLElement & {
2 | [key: string]: any;
3 | };
4 | export type DataWindow = Window & {
5 | [key: string]: any;
6 | };
7 | export type DataDocument = Document & {
8 | [key: string]: any;
9 | };
10 | /**
11 | * 元素数据挂载方法
12 | */
13 | export declare const data: {
14 | /**
15 | * 移除指定数据
16 | * @param {Object} el
17 | * @param {Object} key
18 | */
19 | remove(el: DataHTMLElement | DataWindow | DataDocument, key?: string): void;
20 | /**
21 | * 判断是否含有指定数据
22 | * @param {Object} el
23 | * @param {Object} key
24 | */
25 | has(el: DataHTMLElement | DataWindow | DataDocument, key: string): boolean;
26 | /**
27 | * 获取元素指定数据
28 | * @param {Object} el
29 | * @param {Object} key
30 | */
31 | get(el: DataHTMLElement | DataWindow | DataDocument, key?: string): T;
32 | /**
33 | * 设置元素指定数据
34 | * @param {Object} el
35 | * @param {Object} key
36 | * @param {Object} value
37 | */
38 | set(el: DataHTMLElement | DataWindow | DataDocument, key: string, value: any): void;
39 | };
40 |
--------------------------------------------------------------------------------
/lib/file.d.ts:
--------------------------------------------------------------------------------
1 | export type CompressOptionsType = {
2 | width?: number;
3 | quality?: number;
4 | mimeType?: 'jpeg' | 'webp';
5 | maxSize?: number;
6 | minSize?: number;
7 | };
8 | export type CompressResultType = {
9 | file?: File;
10 | url?: string;
11 | quality?: number;
12 | width?: number;
13 | height?: number;
14 | };
15 | export declare const file: {
16 | /**
17 | * 根据文件获取本地可预览的图片路径
18 | * @param {Object} file
19 | */
20 | getImageUrl(file: File): string;
21 | /**
22 | * 将JS的file对象转为BASE64位字符串,通过then方法回调,参数为base64字符串
23 | * @param {Object} file
24 | */
25 | dataFileToBase64(file: File): Promise;
26 | /**
27 | * 将base64位格式文件转换为file对象
28 | * @param {Object} base64String base64位格式字符串
29 | * @param {Object} fileName 转换后的文件名字,包含后缀
30 | */
31 | dataBase64toFile(base64String: string, fileName: string): File;
32 | /**
33 | * 图片压缩方法
34 | * @param {*} file 需要压缩的图片File文件
35 | * @param {*} options 压缩参数
36 | */
37 | compressImage(file: File, options: CompressOptionsType): Promise;
38 | };
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 so-better
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 |
--------------------------------------------------------------------------------
/lib/common.d.ts:
--------------------------------------------------------------------------------
1 | export type matchingParamType = 'Chinese' | 'chinese' | 'email' | 'username' | 'int+' | 'int-' | 'int' | 'pos' | 'neg' | 'number' | 'phone' | 'idCard' | 'url' | 'IPv4' | 'hex' | 'rgb' | 'rgba' | 'QQ' | 'weixin' | 'plate';
2 | /**
3 | * 常用方法
4 | */
5 | export declare const common: {
6 | /**
7 | * 常用判断
8 | * @param {Object} text 要判断的字符串
9 | * @param {Object} param 判断的类型字符串
10 | */
11 | matchingText(text: string, param: matchingParamType): boolean;
12 | /**
13 | * 根据参数名获取地址栏参数值
14 | * @param {Object} name
15 | */
16 | getUrlParams(name: string): string | undefined;
17 | /**
18 | * 判断是否空对象
19 | * @param {Object} obj
20 | */
21 | isEmptyObject(obj: any): boolean;
22 | /**
23 | * 判断两个参数是否相等
24 | * @param {Object} a
25 | * @param {Object} b
26 | */
27 | equal(a: any, b: any): boolean;
28 | /**
29 | * 是否对象
30 | * @param {Object} val
31 | */
32 | isObject(val: any): boolean;
33 | /**
34 | * 文本复制
35 | * @param {Object} text
36 | */
37 | copyText(text: string): Promise;
38 | /**
39 | * 深度克隆
40 | * @param {Object} data
41 | */
42 | clone(data: T): T;
43 | };
44 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/style.less:
--------------------------------------------------------------------------------
1 | :root {
2 | --vp-home-hero-name-background: linear-gradient(120deg, #8b1bbf 20%, #308af3);
3 | --vp-home-hero-name-color: transparent;
4 | --vp-c-indigo-1: #1269cd;
5 | --vp-c-indigo-2: #1e79e1;
6 | --vp-c-indigo-3: #308af3;
7 |
8 | --vp-code-block-bg: #f7f8fa;
9 |
10 | h5 > .VPBadge.danger {
11 | font-family: var(--vp-font-family-mono);
12 | }
13 |
14 | &.dark {
15 | --vp-code-block-bg: #1f1f22;
16 | }
17 | }
18 |
19 | .demo-button {
20 | display: inline-flex;
21 | justify-content: center;
22 | align-items: center;
23 | padding: 2px 6px;
24 | border: 1px solid fade(#308af3, 30%);
25 | background: fade(#308af3, 10%);
26 | border-radius: 3px;
27 | font-size: 14px;
28 | color: #308af3;
29 | transition: all 300ms;
30 |
31 | &:not(:disabled):focus-visible {
32 | outline: none;
33 | }
34 |
35 | &:not(:disabled):hover {
36 | cursor: pointer;
37 | background: fade(#308af3, 20%);
38 | }
39 |
40 | &:not(:disabled):active {
41 | border: 1px solid fade(#308af3, 50%);
42 | background: fade(#308af3, 30%);
43 | }
44 |
45 | & + .demo-button {
46 | margin-left: 10px;
47 | }
48 |
49 | &:disabled {
50 | opacity: 0.6;
51 | cursor: not-allowed;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/platform.d.ts:
--------------------------------------------------------------------------------
1 | export type OSResultType = {
2 | Windows: boolean;
3 | WindowsCPU?: 'x64' | 'x32';
4 | WindowsVersion?: 'win7' | 'win8' | 'win10';
5 | Mac: boolean;
6 | MacVersion: string;
7 | ios: boolean;
8 | iosVersion: string;
9 | Android: boolean;
10 | AndroidVersion: string;
11 | Linux: boolean;
12 | HarmonyOS: boolean;
13 | Ubuntu: boolean;
14 | };
15 | export declare const platform: {
16 | language(): string;
17 | /**
18 | * 获取设备类型
19 | */
20 | device(): {
21 | PC: boolean;
22 | Mobile: boolean;
23 | iPhone: boolean;
24 | Phone: boolean;
25 | iPad: boolean;
26 | Tablet: boolean;
27 | WindowsPhone: boolean;
28 | };
29 | /**
30 | * 获取浏览器类型
31 | */
32 | browser(): {
33 | Edge: boolean;
34 | Weixin: boolean;
35 | QQ: boolean;
36 | QQBrowser: boolean;
37 | UC: boolean;
38 | Chrome: boolean;
39 | Firefox: boolean;
40 | Sougou: boolean;
41 | Safari: boolean;
42 | };
43 | /**
44 | * 获取浏览器内核
45 | */
46 | browserKernel(): "opera" | "webkit" | "gecko" | undefined;
47 | /**
48 | * 获取操作系统数据
49 | */
50 | os(): OSResultType;
51 | };
52 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | lastUpdated: false
3 | aside: false
4 | layout: home
5 | title: 主页
6 |
7 | hero:
8 | name: dap-util
9 | text: 一个轻量的前端JavaScript工具库
10 | tagline: 专注于JavaScript,不关心UI...
11 | image:
12 | src: https://www.so-better.cn/logo.png
13 | width: 400
14 | actions:
15 | - theme: brand
16 | text: Get Started
17 | link: /guide/introduction
18 | - theme: alt
19 | text: View on GitHub
20 | link: https://github.com/so-better/dap-util
21 |
22 | features:
23 | - title: '@kaitify/core'
24 | details: 基于原生JS的富文本编辑器核心库
25 | link: https://www.so-better.cn/docs/kaitify-core/
26 | - title: '@kaitify/vue'
27 | details: 基于kaitify开发的vue富文本编辑器核心库
28 | link: https://www.so-better.cn/docs/kaitify-vue/
29 | - title: '@kaitify/react'
30 | details: 基于kaitify开发的react富文本编辑器核心库
31 | link: https://www.so-better.cn/docs/kaitify-react/
32 | - title: animator-clip
33 | details: 一个基于 JavaScript 的 requestAnimationFrame API 封装的轻量级 JS 动画插件
34 | link: https://www.so-better.cn/docs/animator-clip/
35 | - title: rem-fit
36 | details: 一款使用rem适配web页面的轻量级插件
37 | link: https://www.so-better.cn/docs/rem-fit/
38 | - title: ruax
39 | details: 一个轻量级的Javascript异步数据请求库
40 | link: https://www.so-better.cn/docs/ruax/
41 | ---
42 |
--------------------------------------------------------------------------------
/docs/guide/install.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 安装
3 | ---
4 |
5 | # 安装
6 |
7 | ## 下载 dap-util 本地到使用
8 |
9 | - 下载地址:[dap-util](https://registry.npmmirror.com/dap-util/download/dap-util-1.6.0.tgz)
10 | - 下载完成后最终解压得到一个 package 文件夹,进入 package 文件夹后,将 package 目录下的整个 lib 目录拷贝到你的项目下
11 | - 在 html 页面中引入 js
12 |
13 | ```html
14 |
15 |
16 | ```
17 |
18 | ```html
19 |
20 |
23 | ```
24 |
25 | ## 通过 CDN 使用 dap-util
26 |
27 | 你可以借助 `script` 标签直接通过 CDN 来使用 `dap-util`
28 |
29 | ```html
30 |
31 |
32 |
33 |
34 | ```
35 |
36 | ```html
37 |
38 |
41 | ```
42 |
43 | ## 通过 npm/yarn/pnpm 安装 dap-util
44 |
45 | > 假设你已了解关于 html、css 和 javascript 的中级知识,并且对于 npm,es6,webpack 已经有了足够的了解,我们更推荐这类安装方式
46 |
47 | ::: code-group
48 |
49 | ```bash [npm]
50 | npm install dap-util
51 |
52 | # 安装指定版本
53 | npm install dap-util@1.6.0
54 | ```
55 |
56 | ```bash [yarn]
57 | yarn install dap-util
58 |
59 | # 安装指定版本
60 | yarn install dap-util@1.6.0
61 | ```
62 |
63 | ```bash [pnpm]
64 | pnpm install dap-util
65 |
66 | # 安装指定版本
67 | pnpm install dap-util@1.6.0
68 | ```
69 |
70 | :::
71 |
--------------------------------------------------------------------------------
/src/data.ts:
--------------------------------------------------------------------------------
1 | const dataName: string = '_dap-datas'
2 |
3 | export type DataHTMLElement = HTMLElement & {
4 | [key: string]: any
5 | }
6 |
7 | export type DataWindow = Window & {
8 | [key: string]: any
9 | }
10 |
11 | export type DataDocument = Document & {
12 | [key: string]: any
13 | }
14 |
15 | /**
16 | * 元素数据挂载方法
17 | */
18 | export const data = {
19 | /**
20 | * 移除指定数据
21 | * @param {Object} el
22 | * @param {Object} key
23 | */
24 | remove(el: DataHTMLElement | DataWindow | DataDocument, key?: string) {
25 | const data = el[dataName] || {}
26 | if (key) {
27 | delete data[key]
28 | el[dataName] = data
29 | } else {
30 | el[dataName] = {}
31 | }
32 | },
33 |
34 | /**
35 | * 判断是否含有指定数据
36 | * @param {Object} el
37 | * @param {Object} key
38 | */
39 | has(el: DataHTMLElement | DataWindow | DataDocument, key: string): boolean {
40 | return (el[dataName] || {}).hasOwnProperty(key)
41 | },
42 |
43 | /**
44 | * 获取元素指定数据
45 | * @param {Object} el
46 | * @param {Object} key
47 | */
48 | get(el: DataHTMLElement | DataWindow | DataDocument, key?: string) {
49 | const data = el[dataName] || {}
50 | return (!!key ? data[key] : data) as T
51 | },
52 |
53 | /**
54 | * 设置元素指定数据
55 | * @param {Object} el
56 | * @param {Object} key
57 | * @param {Object} value
58 | */
59 | set(el: DataHTMLElement | DataWindow | DataDocument, key: string, value: any) {
60 | const data = el[dataName] || {}
61 | data[key] = value
62 | el[dataName] = data
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/docs/guide/modules/color.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: color
3 | ---
4 |
5 | # color
6 |
7 | 颜色模块
8 |
9 | ## rgb2hsv()
10 |
11 | RGB 颜色值转 HSV 颜色值
12 |
13 | - 类型
14 |
15 | ```ts
16 | rgb2hsv(rgb: number[]): number[]
17 | ```
18 |
19 | - 详细信息
20 |
21 | 该方法接收一个长度为 3 的数字数组,如 `[255,0,0]`,表示 RGB 颜色值,最终返回一个长度为 3 的数字数组,表示 HSV 颜色值
22 |
23 | - 示例
24 |
25 | ```ts
26 | import { color } from 'dap-util'
27 | const hsv = color.rgb2hsv([255, 0, 0]) //[0, 100, 100]
28 | ```
29 |
30 | ## hsv2rgb()
31 |
32 | HSV 颜色值转 RGB 颜色值
33 |
34 | - 类型
35 |
36 | ```ts
37 | hsv2rgb(hsv: number[]): number[]
38 | ```
39 |
40 | - 详细信息
41 |
42 | 该方法接收一个长度为 3 的数字数组,如 `[0,100,100]`,表示 HSV 颜色值,最终返回一个长度为 3 的数字数组,表示 RGB 颜色值
43 |
44 | - 示例
45 |
46 | ```ts
47 | import { color } from 'dap-util'
48 | const rgb = color.hsv2rgb([0, 100, 100]) //[255, 0, 0]
49 | ```
50 |
51 | ## rgb2hex()
52 |
53 | RGB 颜色值转 HEX 颜色值
54 |
55 | - 类型
56 |
57 | ```ts
58 | rgb2hex(rgb: number[]): string
59 | ```
60 |
61 | - 详细信息
62 |
63 | 该方法接收一个长度为 3 的数字数组,如 `[255,0,0]`,表示 RGB 颜色值,最终返回一个字符串,表示 HEX 颜色值,即十六进制颜色值
64 |
65 | - 示例
66 |
67 | ```ts
68 | import { color } from 'dap-util'
69 | const hex = color.rgb2hex([255, 0, 0]) //#ff0000
70 | ```
71 |
72 | ## hex2rgb()
73 |
74 | HEX 颜色值转 RGB 颜色值
75 |
76 | - 类型
77 |
78 | ```ts
79 | hex2rgb(hex: string): number[]
80 | ```
81 |
82 | - 详细信息
83 |
84 | 该方法接收一个字符串,表示 HEX 颜色值,即十六进制颜色值,最终返回一个长度为 3 的数字数组,表示 RGB 颜色值
85 |
86 | - 示例
87 |
88 | ```ts
89 | import { color } from 'dap-util'
90 | const rgb = color.hex2rgb('#ff0000') //[255, 0, 0]
91 | ```
92 |
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/_metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "hash": "8ac9bc4a",
3 | "configHash": "b656fdc5",
4 | "lockfileHash": "51eac44b",
5 | "browserHash": "c5c37062",
6 | "optimized": {
7 | "vue": {
8 | "src": "../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
9 | "file": "vue.js",
10 | "fileHash": "3cfad5fa",
11 | "needsInterop": false
12 | },
13 | "vitepress > @vue/devtools-api": {
14 | "src": "../../../../node_modules/@vue/devtools-api/dist/index.js",
15 | "file": "vitepress___@vue_devtools-api.js",
16 | "fileHash": "f7e4ff4b",
17 | "needsInterop": false
18 | },
19 | "vitepress > @vueuse/core": {
20 | "src": "../../../../node_modules/@vueuse/core/index.mjs",
21 | "file": "vitepress___@vueuse_core.js",
22 | "fileHash": "b0e25ed2",
23 | "needsInterop": false
24 | },
25 | "vitepress > @vueuse/integrations/useFocusTrap": {
26 | "src": "../../../../node_modules/@vueuse/integrations/useFocusTrap.mjs",
27 | "file": "vitepress___@vueuse_integrations_useFocusTrap.js",
28 | "fileHash": "b8598fa6",
29 | "needsInterop": false
30 | },
31 | "vitepress > mark.js/src/vanilla.js": {
32 | "src": "../../../../node_modules/mark.js/src/vanilla.js",
33 | "file": "vitepress___mark__js_src_vanilla__js.js",
34 | "fileHash": "de967aaf",
35 | "needsInterop": false
36 | },
37 | "vitepress > minisearch": {
38 | "src": "../../../../node_modules/minisearch/dist/es/index.js",
39 | "file": "vitepress___minisearch.js",
40 | "fileHash": "fd3d6556",
41 | "needsInterop": false
42 | }
43 | },
44 | "chunks": {
45 | "chunk-YJ6QP2VR": {
46 | "file": "chunk-YJ6QP2VR.js"
47 | },
48 | "chunk-LW4I4DCF": {
49 | "file": "chunk-LW4I4DCF.js"
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/docs/guide/modules/speech.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: speech
3 | ---
4 |
5 | # speech
6 |
7 | 语音合成模块
8 |
9 | ## start()
10 |
11 | 将文字加入语音播报队列
12 |
13 | - 类型
14 |
15 | ```ts
16 | start(text: string, options?: SpeechParamsType): void
17 | ```
18 |
19 | - 详细信息
20 |
21 | 第一个入参表示要播报的文字内容,第二个入参表示播报相关的配置属性,包含如下属性:
22 |
23 | - pitch:话语的音调,类型为 `number`
24 | - rate:说话的速度,类型为 `number`
25 | - volume:说话的音量,,类型为 `number`,取值为 0-1
26 | - start:播放开始事件,类型为 `(e: Event, options: EventParamsType) => void`
27 | - end:播放结束事件,类型为 `(e: Event, options: EventParamsType) => void`
28 | - pause:播放暂停事件,类型为 `(e: Event, options: EventParamsType) => void`
29 | - resume:播放恢复事件,类型为 `(e: Event, options: EventParamsType) => void`
30 | - error:播放出错事件,类型为 `(e: Event, options: EventParamsType) => void`
31 |
32 | 上述几个播放事件的第二个参数都是表示当前播放的配置,包含 `text` `pitch` `rate` `volume` 四个属性
33 |
34 | - 示例
35 |
36 | ```ts
37 | import { speech } from 'dap-util'
38 | speech.start('hello')
39 | ```
40 |
41 | ## stop()
42 |
43 | 停止所有播报队列里面的语音
44 |
45 | - 类型
46 |
47 | ```ts
48 | stop(): void
49 | ```
50 |
51 | - 详细信息
52 |
53 | 该方法会停止播报队列里所有的语音
54 |
55 | - 示例
56 |
57 | ```ts
58 | import { speech } from 'dap-util'
59 | speech.stop()
60 | ```
61 |
62 | ## pause()
63 |
64 | 暂停播报
65 |
66 | - 类型
67 |
68 | ```ts
69 | pause(): void
70 | ```
71 |
72 | - 详细信息
73 |
74 | 该方法会暂停当前的语音播报
75 |
76 | - 示例
77 |
78 | ```ts
79 | import { speech } from 'dap-util'
80 | speech.pause()
81 | ```
82 |
83 | ## resume()
84 |
85 | 恢复暂停的播报
86 |
87 | - 类型
88 |
89 | ```ts
90 | resume(): void
91 | ```
92 |
93 | - 详细信息
94 |
95 | 该方法会恢复之前暂停的播报
96 |
97 | - 示例
98 |
99 | ```ts
100 | import { speech } from 'dap-util'
101 | speech.resume()
102 | ```
103 |
--------------------------------------------------------------------------------
/src/string.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 字符串操作
3 | */
4 | export const string = {
5 | /**
6 | * 向指定位置插入字符串
7 | * @param {Object} original 原始字符串
8 | * @param {Object} str 插入的字符串
9 | * @param {Object} index 插入的位置
10 | */
11 | insert(original: string, str: string, index: number) {
12 | if (index < 0) {
13 | throw new Error('The third argument cannot be less than 0')
14 | }
15 | return original.substring(0, index) + str + original.substring(index, original.length)
16 | },
17 |
18 | /**
19 | * 删除指定位置的字符串
20 | * @param {Object} original 原始字符串
21 | * @param {Object} index 删除的位置序列
22 | * @param {Object} num 删除的字符串长度
23 | */
24 | delete(original: string, index: number, num: number) {
25 | if (index < 0) {
26 | throw new Error('The second argument cannot be less than 0')
27 | }
28 | if (num < 0) {
29 | throw new Error('The third argument cannot be less than 0')
30 | }
31 | return original.substring(0, index) + original.substring(index + num, original.length)
32 | },
33 |
34 | /**
35 | * 替换指定位置的字符串
36 | * @param {Object} original 原始字符串
37 | * @param {Object} start 开始位置
38 | * @param {Object} end 结束位置
39 | * @param {Object} str 替换的字符串
40 | */
41 | replace(original: string, start: number, end: number, str: string) {
42 | if (start < 0) {
43 | throw new Error('The second argument cannot be less than 0')
44 | }
45 | if (end < 0) {
46 | throw new Error('The third argument cannot be less than 0')
47 | }
48 | return original.substring(0, start) + str + original.substring(end, original.length)
49 | },
50 |
51 | /**
52 | * 去除字符串空格
53 | * @param {Object} str 原始字符串
54 | * @param {Object} global 为true时去除所有空格,否则只去除两边空格
55 | */
56 | trim(str: string, global?: boolean) {
57 | let result = str.replace(/(^\s+)|(\s+$)/g, '')
58 | if (global) {
59 | result = result.replace(/\s/g, '')
60 | }
61 | return result
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/docs/guide/modules/data.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: data
3 | ---
4 |
5 | # data
6 |
7 | 数据模块
8 |
9 | ## set()
10 |
11 | 在 `dom` 元素或者 `window` 或者 `Document` 上绑定指定数据
12 |
13 | - 类型
14 |
15 | ```ts
16 | set(el: HTMLElement | Window | Document, key: string, value: any): void
17 | ```
18 |
19 | - 详细信息
20 |
21 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示绑定数据的 `key` 值,第三个参数表示绑定的数据
22 |
23 | 该方法会将指定数据绑定到 `dom` 元素或者 `window` 或者 `Document` 上
24 |
25 | - 示例
26 |
27 | ```ts
28 | import { data } from 'dap-util'
29 | data.set(document.body, 'name', 'jack')
30 | ```
31 |
32 | ## get()
33 |
34 | 从 `dom` 元素或者 `window` 或者 `Document` 上获取绑定的指定数据
35 |
36 | - 类型
37 |
38 | ```ts
39 | get(el: HTMLElement | Window | Document, key?: string): T
40 | ```
41 |
42 | - 详细信息
43 |
44 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示需要获取的数据的 `key` 值,如果没有设置第二个参数,则默认返回绑定的全部数据
45 |
46 | - 示例
47 |
48 | ```ts
49 | import { data } from 'dap-util'
50 | const name = data.get(document.body, 'name') //jack
51 | ```
52 |
53 | ## has()
54 |
55 | 判断 `dom` 元素或者 `window` 或者 `Document` 上是否存在绑定的数据
56 |
57 | - 类型
58 |
59 | ```ts
60 | has(el: HTMLElement | Window | Document, key: string): boolean
61 | ```
62 |
63 | - 详细信息
64 |
65 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示需要获取的数据的 `key` 值
66 |
67 | 该方法会判断指定 `key` 值的数据是否在 `dom` 元素或者 `window` 或者 `Document` 上,返回 `boolean` 值
68 |
69 | - 示例
70 |
71 | ```ts
72 | import { data } from 'dap-util'
73 | data.has(document.body, 'name') //true
74 | ```
75 |
76 | ## remove()
77 |
78 | 在 `dom` 元素或者 `window` 或者 `Document` 上移除指定数据
79 |
80 | - 类型
81 |
82 | ```ts
83 | remove(el: HTMLElement | Window | Document, key?: string): void
84 | ```
85 |
86 | - 详细信息
87 |
88 | 第一个入参表示`dom` 元素或者 `window` 或者 `Document`,第二个入参表示需要移除的数据的 `key` 值,如果没有设置第二个参数,则默认移除绑定的全部数据
89 |
90 | - 示例
91 |
92 | ```ts
93 | import { data } from 'dap-util'
94 | data.remove(document.body, 'name')
95 | ```
96 |
--------------------------------------------------------------------------------
/docs/guide/modules/string.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: string
3 | ---
4 |
5 | # string
6 |
7 | 字符串模块
8 |
9 | ## insert()
10 |
11 | 向指定位置插入字符串
12 |
13 | - 类型
14 |
15 | ```ts
16 | insert(original: string, str: string, index: number): string
17 | ```
18 |
19 | - 详细信息
20 |
21 | 第一个入参表示源字符串,第二个参数表示需要插入的字符串,第三个参数表示插入的位置
22 |
23 | 该方法会将指定字符串插入到源字符串中的指定位置,返回新的字符串
24 |
25 | - 示例
26 |
27 | ```ts
28 | import { string } from 'dap-util'
29 | string.insert('这是一个美好的世界', '且充满无限可能', 6) // 这是一个美好且充满无限可能的世界
30 | ```
31 |
32 | ## delete()
33 |
34 | 删除指定位置的字符串
35 |
36 | - 类型
37 |
38 | ```ts
39 | delete(original: string, index: number, num: number): string
40 | ```
41 |
42 | - 详细信息
43 |
44 | 第一个入参表示源字符串,第二个参数表示从哪个位置删除,第三个参数表示删除字符串的长度
45 |
46 | 该方法会在删除指定位置的字符串后返回新的字符串
47 |
48 | - 示例
49 |
50 | ```ts
51 | import { string } from 'dap-util'
52 | string.delete('这是一个美好且充满无限可能的世界', 6, 7) //这是一个美好的世界
53 | ```
54 |
55 | ## replace()
56 |
57 | 替换指定位置的字符串
58 |
59 | - 类型
60 |
61 | ```ts
62 | replace(original: string, start: number, end: number, str: string): string
63 | ```
64 |
65 | - 详细信息
66 |
67 | 第一个入参表示源字符串,第二个参数表示替换开始的位置,第三个参数表示替换截止的位置,第四个参数表示替换的字符串
68 |
69 | 该方法会将原字符串从开始位置到截止为止之间的字符串删除,然后将替换的字符串插入其中
70 |
71 | - 示例
72 |
73 | ```ts
74 | import { string } from 'dap-util'
75 | string.replace('这是一个美好且充满无限可能的世界', 4, 13, '糟糕') //这是一个糟糕的世界
76 | ```
77 |
78 | ## trim()
79 |
80 | 去除字符串的空格
81 |
82 | - 类型
83 |
84 | ```ts
85 | trim(str: string, global?: boolean): string
86 | ```
87 |
88 | - 详细信息
89 |
90 | 第一个入参表示需要去除空格的字符串,第二个入参表示是否去去除全部的空格,如果为 `false` 或者不设置则只去除字符串两侧的空格
91 |
92 | 该方法会返回去除空格后的新字符串
93 |
94 | - 示例
95 |
96 | ```ts
97 | import { string } from 'dap-util'
98 | //去除两边的空格
99 | const result = string.trim(' he llo ') // 'he llo'
100 | //去除所有空格
101 | const result = string.trim(' he llo ', true) // 'hello'
102 | ```
103 |
--------------------------------------------------------------------------------
/docs/guide/modules/event.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: event
3 | ---
4 |
5 | # event
6 |
7 | 事件模块
8 |
9 | ## on()
10 |
11 | 给元素绑定事件
12 |
13 | - 类型
14 |
15 | ```ts
16 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions): void
17 | ```
18 |
19 | - 详细信息
20 |
21 | 第一个入参表示绑定事件的元素,同时支持 `window` 和 `document`;第二个入参表示事件名称,如“click”;第三个参数是可选参数,表示事件修饰参数,等同于 `addEventListener` 函数的第三个参数
22 |
23 | 第二个参数在设置事件名称时,支持设置事件修饰名称,如“click.a”,后面的“a”就是修饰名称,同时也支持绑定多个事件,每个事件之间通过空格隔开
24 |
25 | - 示例
26 |
27 | ```ts
28 | import { event } from 'dap-util'
29 | //设置事件
30 | event.on(document.body, 'click', e => {
31 | console.log('click')
32 | })
33 | //设置事件修饰名称
34 | event.on(document.body, 'mousedown.a', e => {
35 | console.log('mousedown.a')
36 | })
37 | //绑定多个事件
38 | event.on(document.body, 'mousedown click.a', e => {
39 | console.log('mousedown.a')
40 | })
41 | ```
42 |
43 | ## off()
44 |
45 | 解绑元素事件
46 |
47 | - 类型
48 |
49 | ```ts
50 | off(el: HTMLElement | Window | Document, eventName?: string): void
51 | ```
52 |
53 | - 详细信息
54 |
55 | 第一个入参表示解绑事件的元素,同时支持 `window` 和 `document`;第二个入参表示事件名称,如“click”
56 |
57 | 第二个参数支持设置事件修饰名称,如“click.a”,后面的“a”就是修饰名称,同时也支持解绑多个事件,每个事件之间通过空格隔开
58 |
59 | - 示例
60 |
61 | ```ts
62 | import { event } from 'dap-util'
63 | //解绑事件
64 | event.off(document.body, 'click')
65 | //解绑带事件修饰名称的事件
66 | event.off(document.body, 'mousedown.a')
67 | //同时解绑多个事件
68 | event.off(document.body, 'mousedown click.a')
69 | ```
70 |
71 | ## get()
72 |
73 | 获取元素绑定的所有事件
74 |
75 | - 类型
76 |
77 | ```ts
78 | get(el: HTMLElement | Window | Document): EventObjType | undefined
79 | ```
80 |
81 | - 详细信息
82 |
83 | 提供一个入参,表示获取事件的元素,同时支持 `window` 和 `document`,该方法返回一个对象,每个 key 表示带修饰名称的事件名称,每个值表示事件配置,包含 `type`(事件名,如“click”) `fn`(执行函数) `options`(在 on 方法中设置的第三个参数)三个属性
84 |
85 | - 示例
86 |
87 | ```ts
88 | import { event } from 'dap-util'
89 | const events = event.get(document.body)
90 | ```
91 |
--------------------------------------------------------------------------------
/docs/guide/modules/file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: file
3 | ---
4 |
5 | # file
6 |
7 | 文件模块
8 |
9 | ## getImageUrl()
10 |
11 | 根据文件获取本地可预览的图片路径
12 |
13 | - 类型
14 |
15 | ```ts
16 | getImageUrl(file: File): string
17 | ```
18 |
19 | - 详细信息
20 |
21 | 提供一个入参,类型为 `File`,该方法返回该文件的本地可预览地址
22 |
23 | - 示例
24 |
25 | ```ts
26 | import { file } from 'dap-util'
27 | const url = file.getImageUrl(imageFile)
28 | ```
29 |
30 | ## dataFileToBase64()
31 |
32 | `file` 对象转 `base64` 字符串
33 |
34 | - 类型
35 |
36 | ```ts
37 | dataFileToBase64(file: File): Promise
38 | ```
39 |
40 | - 详细信息
41 |
42 | 提供一个入参,类型为 `File`,表示需要转换的文件对象,该方法返回一个 `Promise` 对象,通过该对象回调可以获得 `base64` 字符串
43 |
44 | - 示例
45 |
46 | ```ts
47 | import { file } from 'dap-util'
48 | const base64String = await file.dataFileToBase64(imageFile)
49 | //或者
50 | file.dataFileToBase64(imageFile).then(res => {
51 | const base64String = res
52 | })
53 | ```
54 |
55 | ## dataBase64toFile()
56 |
57 | `base64` 字符串转 `file` 对象
58 |
59 | - 类型
60 |
61 | ```ts
62 | dataBase64toFile(base64String: string, fileName: string): File
63 | ```
64 |
65 | - 详细信息
66 |
67 | 第一个入参表示需要转换的 `base64` 字符串,第二个入参表示转换成文件的文件名称,该方法返回一个 `File` 对象
68 |
69 | - 示例
70 |
71 | ```ts
72 | import { file } from 'dap-util'
73 | const image = file.dataBase64toFile(,'image.png')
74 | ```
75 |
76 | ## compressImage()
77 |
78 | 图片压缩
79 |
80 | - 类型
81 |
82 | ```ts
83 | compressImage(file: File, options: CompressOptionsType): Promise
84 | ```
85 |
86 | - 详细信息
87 |
88 | 第一个入参表示需要压缩的图片文件,第二个入参是压缩的相关配置,具体包含如下属性:
89 |
90 | - `width`:类型为 `number`,表示压缩图片的宽,单位 `px`,如果不设置默认为原图宽
91 | - `quality`:类型为 `number`,取值 0-1,表示压缩图片质量,默认为原图的 0.8
92 | - `mimeType`:压缩图片的类型,可取值 `jpeg` `webp`,默认为 `jpeg`
93 | - `maxSize`:类型为 `number`,表示压缩后的最大值,单位 `kb`,默认为 0 表示不设置此值
94 | - `minSize`:类型为 `number`,表示压缩后的最小值,小于该大小的图片不进行压缩,单位 `kb`,默认为 0 表示任何图片都要压缩
95 |
96 | - 示例
97 |
98 | ```ts
99 | import { file } from 'dap-util'
100 | const newImage = file.compressImage(image, {
101 | width: 800,
102 | quality: 0.5
103 | })
104 | ```
105 |
--------------------------------------------------------------------------------
/docs/guide/modules/number.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: number
3 | ---
4 |
5 | # number
6 |
7 | 数字模块
8 |
9 | ## formatNumber()
10 |
11 | 数字格式化
12 |
13 | - 类型
14 |
15 | ```ts
16 | formatNumber(num: number): string
17 | ```
18 |
19 | - 详细信息
20 |
21 | 提供一个入参,类型为 `number`,表示需要格式化的数字,该方法会返回该数字格式化后的字符串
22 |
23 | 所谓的格式化,即每隔 3 位数给数值加上逗号
24 |
25 | - 示例
26 |
27 | ```ts
28 | import { number } from 'dap-util'
29 | const formatNum = number.formatNumber(100000) //1000,00
30 | ```
31 |
32 | ## isNumber()
33 |
34 | 判断是否数字
35 |
36 | - 类型
37 |
38 | ```ts
39 | isNumber(num: any): boolean
40 | ```
41 |
42 | - 详细信息
43 |
44 | 提供一个任意类型的入参,判断是否数字类型,返回 `boolean` 值
45 |
46 | - 示例
47 |
48 | ```ts
49 | import { number } from 'dap-util'
50 | number.isNumber(100) //true
51 | number.isNumber('100') //false
52 | number.isNumber(NaN) //false
53 | ```
54 |
55 | ## add()
56 |
57 | 多个数的加法运算
58 |
59 | - 类型
60 |
61 | ```ts
62 | add(...values: number[]): number
63 | ```
64 |
65 | - 详细信息
66 |
67 | 将多个数进行累加,参数可以是任意多个,必须都是数字类型,该方法返回累加后的数值
68 |
69 | - 示例
70 |
71 | ```ts
72 | import { number } from 'dap-util'
73 | number.add(1, 1, 1) //3
74 | number.add(1, 10, 21) //32
75 | ```
76 |
77 | ## subtract()
78 |
79 | 多个数的减法运算
80 |
81 | - 类型
82 |
83 | ```ts
84 | subtract(...values: number[]): number
85 | ```
86 |
87 | - 详细信息
88 |
89 | 将多个数进行累减,参数可以是任意多个,必须都是数字类型,该方法返回累减后的数值
90 |
91 | - 示例
92 |
93 | ```ts
94 | import { number } from 'dap-util'
95 | number.subtract(1, 1, 1) //-1
96 | number.subtract(1, 10, 21) //-30
97 | ```
98 |
99 | ## mutiply()
100 |
101 | 多个数的乘法运算
102 |
103 | - 类型
104 |
105 | ```ts
106 | mutiply(...values: number[]): number
107 | ```
108 |
109 | - 详细信息
110 |
111 | 将多个数进行累乘,参数可以是任意多个,必须都是数字类型,该方法返回累乘后的数值
112 |
113 | - 示例
114 |
115 | ```ts
116 | import { number } from 'dap-util'
117 | number.mutiply(1, 1, 1) //1
118 | number.mutiply(1, 10, 21) //210
119 | ```
120 |
121 | ## divide()
122 |
123 | 多个数的除法运算
124 |
125 | - 类型
126 |
127 | ```ts
128 | divide(...values: number[]): number
129 | ```
130 |
131 | - 详细信息
132 |
133 | 将多个数进行累除,参数可以是任意多个,必须都是数字类型,该方法返回累除后的数值
134 |
135 | - 示例
136 |
137 | ```ts
138 | import { number } from 'dap-util'
139 | number.divide(9, 3, 2) //1.5
140 | number.divide(100, 1000, 2) //0.05
141 | ```
142 |
--------------------------------------------------------------------------------
/src/number.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 数字相关方法
3 | */
4 | export const number = {
5 | /**
6 | * 数字格式化
7 | * @param {Number} num
8 | */
9 | formatNumber(num: number) {
10 | if (this.isNumber(num)) {
11 | return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
12 | }
13 | return num.toString()
14 | },
15 |
16 | /**
17 | * 判断是否数字
18 | * @param {Object} num
19 | */
20 | isNumber(num: any) {
21 | if (typeof num == 'number' && !isNaN(num)) {
22 | return true
23 | }
24 | return false
25 | },
26 |
27 | /**
28 | * 多个数的加法运算
29 | */
30 | add(...values: number[]) {
31 | return values.reduce((num, value) => {
32 | let r1 = 0
33 | let r2 = 0
34 | let m = 0
35 | try {
36 | r1 = num.toString().split('.')[1].length
37 | } catch (e) {}
38 | try {
39 | r2 = value.toString().split('.')[1].length
40 | } catch (e) {}
41 | m = Math.pow(10, Math.max(r1, r2))
42 | return (num * m + value * m) / m
43 | })
44 | },
45 |
46 | /**
47 | * 多个数的减法运算
48 | */
49 | subtract(...values: number[]) {
50 | return values.reduce((num, value) => {
51 | let r1 = 0
52 | let r2 = 0
53 | let m = 0
54 | try {
55 | r1 = num.toString().split('.')[1].length
56 | } catch (e) {}
57 | try {
58 | r2 = value.toString().split('.')[1].length
59 | } catch (e) {}
60 | m = Math.pow(10, Math.max(r1, r2))
61 | return (num * m - value * m) / m
62 | })
63 | },
64 |
65 | /**
66 | * 多个数的乘法运算
67 | */
68 | mutiply(...values: number[]) {
69 | return values.reduce((num, value) => {
70 | let m = 0
71 | let s1 = num.toString()
72 | let s2 = value.toString()
73 | try {
74 | m += s1.split('.')[1].length
75 | } catch (e) {}
76 | try {
77 | m += s2.split('.')[1].length
78 | } catch (e) {}
79 | return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m)
80 | })
81 | },
82 |
83 | /**
84 | * 多个数的除法运算
85 | */
86 | divide(...values: number[]) {
87 | return values.reduce((num, value) => {
88 | let t1 = 0
89 | let t2 = 0
90 | let s1 = num.toString()
91 | let s2 = value.toString()
92 | try {
93 | t1 = s1.split('.')[1].length
94 | } catch (e) {}
95 | try {
96 | t2 = s2.split('.')[1].length
97 | } catch (e) {}
98 | return (Number(s1.replace('.', '')) / Number(s2.replace('.', ''))) * Math.pow(10, t2 - t1)
99 | })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/docs/guide/modules/platform.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: platform
3 | ---
4 |
5 | # platform
6 |
7 | 平台终端模块
8 |
9 | ## language()
10 |
11 | 获取设备语言类型
12 |
13 | - 类型
14 |
15 | ```ts
16 | language(): string
17 | ```
18 |
19 | - 详细信息
20 |
21 | 该方法返回当前浏览器的语言类型
22 |
23 | - 示例
24 |
25 | ```ts
26 | import { platform } from 'dap-util'
27 | platform.language() //zh-CN
28 | ```
29 |
30 | ## device()
31 |
32 | 获取设备类型
33 |
34 | - 类型
35 |
36 | ```ts
37 | device(): {
38 | PC: boolean;
39 | Mobile: boolean;
40 | iPhone: boolean;
41 | Phone: boolean;
42 | iPad: boolean;
43 | Tablet: boolean;
44 | WindowsPhone: boolean;
45 | }
46 | ```
47 |
48 | - 详细信息
49 |
50 | 该方法会判断当前浏览器所在的设备类型,并返回一个对象,该对象包含如下属性:
51 |
52 | - `PC`:是否 `PC` 电脑端
53 | - `Mobile`:是否移动端
54 | - `iPhone`:是否 `iPhone`
55 | - `Phone`:是否手机
56 | - `iPad`:是否 `iPad`
57 | - `Tablet`:是否平板电脑
58 | - `WindowsPhone`:是否 `s` 系统手机
59 |
60 | - 示例
61 |
62 | ```ts
63 | import { platform } from 'dap-util'
64 | platform.device() //{ PC: true, Mobile: false, iPhone: false, Phone: false, iPad: false, Tablet: false, WindowsPhone: false}
65 | ```
66 |
67 | ## browser()
68 |
69 | 获取浏览器类型
70 |
71 | - 类型
72 |
73 | ```ts
74 | browser(): {
75 | Edge: boolean;
76 | Weixin: boolean;
77 | QQ: boolean;
78 | QQBrowser: boolean;
79 | UC: boolean;
80 | Chrome: boolean;
81 | Firefox: boolean;
82 | Sougou: boolean;
83 | Safari: boolean;
84 | }
85 | ```
86 |
87 | - 详细信息
88 |
89 | 该方法会判断当前浏览器类型,并返回一个对象,该对象包含如下属性:
90 |
91 | - `Edge`:是否 `edge` 浏览器
92 | - `Weixin`:是否微信内置浏览器
93 | - `QQ`:是否 `QQ` 内置浏览器
94 | - `QQBrowser`:是否 `QQ` 浏览器
95 | - `UC`:是否 `UC` 浏览器
96 | - `Chrome`:是否谷歌浏览器
97 | - `Firefox`:是否火狐浏览器
98 | - `Sougou`:是否搜狗浏览器
99 | - `Safari`:是否 `safari` 浏览器
100 |
101 | - 示例
102 |
103 | ```ts
104 | import { platform } from 'dap-util'
105 | platform.browser() //{ Edge: false, Weixin: false, QQ: false, QQBrowser: false, UC: false, Chrome: true, Firefox: false, Sougou: false, Safari: false }
106 | ```
107 |
108 | ## browserKernel()
109 |
110 | 获取浏览器内核
111 |
112 | - 类型
113 |
114 | ```ts
115 | browserKernel(): "opera" | "webkit" | "gecko" | undefined
116 | ```
117 |
118 | - 详细信息
119 |
120 | 该方法用于获取当前浏览器内核,返回值可取值为 `opera` `webkit` `gecko`,如果内核都不是这三者,则返回 `undefined`
121 |
122 | - 示例
123 |
124 | ```ts
125 | import { platform } from 'dap-util'
126 | platform.browserKernel() //webkit
127 | ```
128 |
129 | ## os()
130 |
131 | 获取操作系统数据
132 |
133 | - 类型
134 |
135 | ```ts
136 | os(): OSResultType
137 | ```
138 |
139 | - 详细信息
140 |
141 | 该方法会返回当前浏览器所在系统的数据,包含如下属性:
142 |
143 | - `Windows`:是否 `windows` 系统
144 | - `WindowsCPU`:`windows` 系统的 cpu 类型,值为“x64”或者“x32”
145 | - `WindowsVersion`:`windows` 系统的版本,可取值 `win7` `win8` `win10`
146 | - Mac:是否 `Mac` 系统
147 | - MacVersion:`Mac` 系统版本号
148 | - ios:是否 `ios` 系统
149 | - iosVersion:`ios` 系统版本号
150 | - Android:是否安卓系统
151 | - AndroidVersion:安卓系统版本号
152 | - Linux:是否 `linux` 系统
153 | - HarmonyOS:是否鸿蒙系统
154 | - Ubuntu:是否 `unbuntu` 系统
155 |
156 | - 示例
157 |
158 | ```ts
159 | import { platform } from 'dap-util'
160 | const { Mac } = platform.os() //判断是否Mac系统
161 | ```
162 |
--------------------------------------------------------------------------------
/src/speech.ts:
--------------------------------------------------------------------------------
1 | export type EventParamsType = {
2 | text: string
3 | pitch: number
4 | rate: number
5 | volume: number
6 | }
7 |
8 | export type SpeechParamsType = {
9 | //话语的音调
10 | pitch?: number
11 | //说话的速度
12 | rate?: number
13 | //说话的音量:0-1
14 | volume?: number
15 | //播放开始事件
16 | start?: (e: Event, options: EventParamsType) => void
17 | //播放结束事件
18 | end?: (e: Event, options: EventParamsType) => void
19 | //播放暂停事件
20 | pause?: (e: Event, options: EventParamsType) => void
21 | //播放恢复事件
22 | resume?: (e: Event, options: EventParamsType) => void
23 | //播放出错事件
24 | error?: (e: Event, options: EventParamsType) => void
25 | }
26 |
27 | /**
28 | * 语音合成方法
29 | */
30 | export const speech = {
31 | /**
32 | * 将文字加入语音播报队列
33 | * @param {Object} text
34 | * @param {Object} options
35 | */
36 | start(text: string, options?: SpeechParamsType) {
37 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
38 | throw new Error('The current browser does not support this syntax')
39 | }
40 | const speech = new SpeechSynthesisUtterance()
41 | speech.text = text
42 | speech.pitch = options?.pitch ?? 0.8
43 | speech.rate = options?.rate ?? 1
44 | speech.volume = options?.volume ?? 1
45 | speech.lang = 'zh-CN'
46 | //播放开始后调用
47 | speech.onstart = event => {
48 | options?.start?.apply(speech, [
49 | event,
50 | {
51 | text: text,
52 | pitch: options.pitch ?? 0.8,
53 | rate: options.rate ?? 1,
54 | volume: options.volume ?? 1
55 | }
56 | ])
57 | }
58 | //播放结束后调用
59 | speech.onend = event => {
60 | options?.end?.apply(speech, [
61 | event,
62 | {
63 | text: text,
64 | pitch: options.pitch ?? 0.8,
65 | rate: options.rate ?? 1,
66 | volume: options.volume ?? 1
67 | }
68 | ])
69 | }
70 | //播放暂停后调用
71 | speech.onpause = event => {
72 | options?.pause?.apply(speech, [
73 | event,
74 | {
75 | text: text,
76 | pitch: options.pitch ?? 0.8,
77 | rate: options.rate ?? 1,
78 | volume: options.volume ?? 1
79 | }
80 | ])
81 | }
82 | //播放恢复后调用
83 | speech.onresume = event => {
84 | options?.resume?.apply(speech, [
85 | event,
86 | {
87 | text: text,
88 | pitch: options.pitch ?? 0.8,
89 | rate: options.rate ?? 1,
90 | volume: options.volume ?? 1
91 | }
92 | ])
93 | }
94 | //播放出错后调用
95 | speech.onerror = event => {
96 | options?.error?.apply(speech, [
97 | event,
98 | {
99 | text: text,
100 | pitch: options.pitch ?? 0.8,
101 | rate: options.rate ?? 1,
102 | volume: options.volume ?? 1
103 | }
104 | ])
105 | }
106 | //加入播放队列
107 | window.speechSynthesis.speak(speech)
108 | },
109 | /**
110 | * 停止播报,停止所有播报队列里面的语音
111 | */
112 | stop() {
113 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
114 | throw new Error('The current browser does not support this syntax')
115 | }
116 | window.speechSynthesis.cancel()
117 | },
118 |
119 | /**
120 | * 暂停播报
121 | */
122 | pause() {
123 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
124 | throw new Error('The current browser does not support this syntax')
125 | }
126 | window.speechSynthesis.pause()
127 | },
128 |
129 | /**
130 | * 恢复暂停的播报
131 | */
132 | resume() {
133 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
134 | throw new Error('The current browser does not support this syntax')
135 | }
136 | window.speechSynthesis.resume()
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/color.ts:
--------------------------------------------------------------------------------
1 | import { common as commonUtil } from './common'
2 | /**
3 | * 颜色相关方法
4 | */
5 | export const color = {
6 | /**
7 | * rgb转hsv值
8 | * @param {Object} rgb rgb值,数组
9 | */
10 | rgb2hsv(rgb: number[]) {
11 | if (rgb.length != 3) {
12 | throw new TypeError('Invalid argument')
13 | }
14 | let h = 0
15 | let s = 0
16 | let v = 0
17 | let r = rgb[0] >= 255 ? 255 : rgb[0]
18 | let g = rgb[1] >= 255 ? 255 : rgb[1]
19 | let b = rgb[2] >= 255 ? 255 : rgb[2]
20 | r = r <= 0 ? 0 : r
21 | g = g <= 0 ? 0 : g
22 | b = b <= 0 ? 0 : b
23 | let max = Math.max(r, g, b)
24 | let min = Math.min(r, g, b)
25 | v = max / 255
26 | if (max === 0) {
27 | s = 0
28 | } else {
29 | s = 1 - min / max
30 | }
31 | if (max === min) {
32 | h = 0 //事实上,max===min的时候,h无论为多少都无所谓
33 | } else if (max === r && g >= b) {
34 | h = 60 * ((g - b) / (max - min)) + 0
35 | } else if (max === r && g < b) {
36 | h = 60 * ((g - b) / (max - min)) + 360
37 | } else if (max === g) {
38 | h = 60 * ((b - r) / (max - min)) + 120
39 | } else if (max === b) {
40 | h = 60 * ((r - g) / (max - min)) + 240
41 | }
42 | return [h, s * 100, v * 100]
43 | },
44 |
45 | /**
46 | * hsv格式值转rgb值
47 | * @param {Object} hsv hsv值,数组
48 | */
49 | hsv2rgb(hsv: number[]) {
50 | if (hsv.length != 3) {
51 | throw new TypeError('Invalid argument')
52 | }
53 | let h = hsv[0] >= 360 || hsv[0] <= 0 ? 0 : hsv[0]
54 | let s = hsv[1] >= 100 ? 100 : hsv[1]
55 | s = s <= 0 ? 0 : s
56 | let v = hsv[2] >= 100 ? 100 : hsv[2]
57 | v = v <= 0 ? 0 : v
58 | s = s / 100
59 | v = v / 100
60 | let r = 0
61 | let g = 0
62 | let b = 0
63 | let i = parseInt(((h / 60) % 6) + '')
64 | let f = h / 60 - i
65 | let p = v * (1 - s)
66 | let q = v * (1 - f * s)
67 | let t = v * (1 - (1 - f) * s)
68 | switch (i) {
69 | case 0:
70 | r = v
71 | g = t
72 | b = p
73 | break
74 | case 1:
75 | r = q
76 | g = v
77 | b = p
78 | break
79 | case 2:
80 | r = p
81 | g = v
82 | b = t
83 | break
84 | case 3:
85 | r = p
86 | g = q
87 | b = v
88 | break
89 | case 4:
90 | r = t
91 | g = p
92 | b = v
93 | break
94 | case 5:
95 | r = v
96 | g = p
97 | b = q
98 | break
99 | default:
100 | break
101 | }
102 | r = parseInt(r * 255 + '')
103 | g = parseInt(g * 255 + '')
104 | b = parseInt(b * 255 + '')
105 | return [r, g, b]
106 | },
107 |
108 | /**
109 | * rgb值转十六进制
110 | * @param {Array} rgb rgb值,数组
111 | */
112 | rgb2hex(rgb: number[]) {
113 | if (rgb.length != 3) {
114 | throw new TypeError('Invalid argument')
115 | }
116 | let r = rgb[0]
117 | let g = rgb[1]
118 | let b = rgb[2]
119 | let hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)
120 | return hex
121 | },
122 |
123 | /**
124 | * 十六进制颜色转rgb
125 | * @param {String} hex 十六进制颜色值
126 | */
127 | hex2rgb(hex: string) {
128 | let color = hex.toLowerCase()
129 | if (!commonUtil.matchingText(color, 'hex')) {
130 | throw new TypeError('The argument must be a hexadecimal color value')
131 | }
132 | //4位数的十六进制颜色值
133 | if (color.length === 4) {
134 | let colorNew = '#'
135 | for (let i = 1; i < 4; i += 1) {
136 | colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1))
137 | }
138 | color = colorNew
139 | }
140 | //处理六位的颜色值
141 | let colorChange = []
142 | for (let i = 1; i < 7; i += 2) {
143 | colorChange.push(parseInt('0x' + color.slice(i, i + 2)))
144 | }
145 | return colorChange
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitepress'
2 |
3 | export default defineConfig({
4 | base: '/docs/dap-util/',
5 | title: 'dap-util',
6 | description: '一个轻量的前端JavaScript工具库,专注于JavaScript,不关心UI',
7 | lastUpdated: true,
8 | head: [
9 | ['link', { rel: 'icon', type: 'image/png', href: 'https://www.so-better.cn/ico.png' }],
10 | ['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no' }]
11 | ],
12 | outDir: 'dap-util',
13 | cleanUrls: true,
14 | themeConfig: {
15 | logo: {
16 | src: 'https://www.so-better.cn/logo.png'
17 | },
18 | outline: {
19 | label: '本页目录',
20 | level: [2, 5]
21 | },
22 | nav: [
23 | { text: '指南', link: '/guide/introduction', activeMatch: '/guide' },
24 | { text: '更新日志', link: '/changelog' }
25 | ],
26 | sidebar: {
27 | '/guide': [
28 | {
29 | text: '开始使用',
30 | items: [
31 | {
32 | text: '简介',
33 | link: '/guide/introduction'
34 | },
35 | {
36 | text: '安装',
37 | link: '/guide/install'
38 | },
39 | {
40 | text: '快速上手',
41 | link: '/guide/quick-start'
42 | }
43 | ]
44 | },
45 | {
46 | text: '模块',
47 | items: [
48 | {
49 | text: 'color',
50 | link: '/guide/modules/color'
51 | },
52 | {
53 | text: 'common',
54 | link: '/guide/modules/common'
55 | },
56 | {
57 | text: 'data',
58 | link: '/guide/modules/data'
59 | },
60 | {
61 | text: 'element',
62 | link: '/guide/modules/element'
63 | },
64 | {
65 | text: 'event',
66 | link: '/guide/modules/event'
67 | },
68 | {
69 | text: 'file',
70 | link: '/guide/modules/file'
71 | },
72 | {
73 | text: 'number',
74 | link: '/guide/modules/number'
75 | },
76 | {
77 | text: 'platform',
78 | link: '/guide/modules/platform'
79 | },
80 | {
81 | text: 'speech',
82 | link: '/guide/modules/speech'
83 | },
84 | {
85 | text: 'string',
86 | link: '/guide/modules/string'
87 | }
88 | ]
89 | }
90 | ]
91 | },
92 | socialLinks: [
93 | { icon: 'npm', link: 'https://www.npmjs.com/package/dap-util' },
94 | { icon: 'gitee', link: 'https://gitee.com/so-better/dap-util' },
95 | { icon: 'github', link: 'https://github.com/so-better/dap-util' }
96 | ],
97 | search: { provider: 'local' },
98 | lastUpdated: {
99 | text: '上次更新'
100 | },
101 | docFooter: {
102 | prev: 'Prev',
103 | next: 'Next'
104 | },
105 | darkModeSwitchTitle: '切换到深色模式',
106 | lightModeSwitchTitle: '切换到浅色模式',
107 | darkModeSwitchLabel: '主题风格切换',
108 | sidebarMenuLabel: '菜单目录',
109 | returnToTopLabel: '返回顶部',
110 | externalLinkIcon: true
111 | },
112 | markdown: {
113 | image: {
114 | lazyLoading: true
115 | },
116 | theme: {
117 | dark: 'github-dark',
118 | light: 'github-light'
119 | },
120 | codeCopyButtonTitle: '复制代码'
121 | },
122 | vite: {
123 | server: {
124 | port: 5402
125 | }
126 | }
127 | })
128 |
--------------------------------------------------------------------------------
/docs/guide/modules/common.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: common
3 | ---
4 |
5 | # common
6 |
7 | 通用模块
8 |
9 | ## matchingText()
10 |
11 | 常用正则匹配判断
12 |
13 | - 类型
14 |
15 | ```ts
16 | matchingText(text: string, param: matchingParamType): boolean
17 | ```
18 |
19 | - 详细信息
20 |
21 | 第一个入参表示需要匹配的字符串,第二个入参表示匹配的类型,该方法返回一个 `boolean` 值
22 |
23 | 关于匹配的类型,取值范围如下:
24 |
25 | `Chinese`:判断字符串是否为中文
26 |
27 | `chinese`:判断字符串是否包含中文
28 |
29 | `email`:判断字符串是否为邮箱
30 |
31 | `username`:判断字符串是否为 4-16 位的用户名(字母数字下划线)
32 |
33 | `int+`:判断字符串是否为正整数
34 |
35 | `int`:判断字符串是否为整数
36 |
37 | `int-`:判断字符串是否为负整数
38 |
39 | `pos`:判断字符串是否为正数
40 |
41 | `neg`:判断字符串是否为负数
42 |
43 | `number`:判断字符串是否为数字
44 |
45 | `phone`:判断字符串是否为手机号
46 |
47 | `idCard`:判断字符串是否为身份证号码
48 |
49 | `url`:判断字符串是否为网址
50 |
51 | `IPv4`:判断字符串是否为 ip 地址
52 |
53 | `hex`:判断字符串是否为十六进制颜色值
54 |
55 | `rgb`:判断字符串是否为 rgb 值
56 |
57 | `rgba`:判断字符串是否为 rgba 值
58 |
59 | `QQ`:判断字符串是否为 QQ 号码
60 |
61 | `weixin`:判断字符串是否为微信号,6 至 20 位,以字母开头,字母,数字,减号,下划线
62 |
63 | `plate`:判断字符串是否为车牌号
64 |
65 | - 示例
66 |
67 | ```ts
68 | import { common } from 'dap-util'
69 | common.matchingText('rgb(255, 0, 0)', 'rgb') //true
70 | ```
71 |
72 | ## getUrlParams()
73 |
74 | 根据参数名获取地址栏参数值
75 |
76 | - 类型
77 |
78 | ```ts
79 | getUrlParams(name: string): string | undefined
80 | ```
81 |
82 | - 详细信息
83 |
84 | 提供一个入参,类型为 `string`,表示要获取的地址栏参数名称,该方法返回一个参数值,如果地址栏没有该参数则返回 `undefined`
85 |
86 | 该方法本质上可以兼容 `hash` 地址并且参数在 `hash` 后面的情况,但是如果 `hash` 前面也有参数,则不会考虑 `hash` 后面的参数
87 |
88 | - 示例
89 |
90 | ```ts
91 | import { common } from 'dap-util'
92 | const name = common.getUrlParams('name')
93 | ```
94 |
95 | ## isEmptyObject()
96 |
97 | 判断是否空对象
98 |
99 | - 类型
100 |
101 | ```ts
102 | isEmptyObject(obj: any): boolean
103 | ```
104 |
105 | - 详细信息
106 |
107 | 提供一个任意类型的入参,判断是否为空对象,返回 `boolean` 值
108 |
109 | 所谓的空对象,即该参数的 `typeof` 是 `object`,但是该参数没有任何属性
110 |
111 | - 示例
112 |
113 | ```ts
114 | import { common } from 'dap-util'
115 | common.isEmptyObject({}) //true
116 | common.isEmptyObject({ name: 'a' }) //false
117 | common.isEmptyObject('name') //false
118 | ```
119 |
120 | ## equal()
121 |
122 | 判断两个参数是否相等
123 |
124 | - 类型
125 |
126 | ```ts
127 | equal(a: any, b: any): boolean
128 | ```
129 |
130 | - 详细信息
131 |
132 | 提供两个任意类型的入参,该方法会对这两个入参进行比较,判断是否相等,返回 `boolean` 值
133 |
134 | - 示例
135 |
136 | ```ts
137 | import { common } from 'dap-util'
138 | common.equal(1, '1') //false
139 | common.equal(1, 1) //true
140 | common.equal({ name: 'a' }, { name: 'b' }) //false
141 | common.equal({ name: 'a' }, { name: 'a' }) //true
142 | ```
143 |
144 | ## isObject()
145 |
146 | 判断是否对象
147 |
148 | - 类型
149 |
150 | ```ts
151 | isObject(val: any): boolean
152 | ```
153 |
154 | - 详细信息
155 |
156 | 提供一个任意类型的入参,该方法会判断它是否为对象,返回 `boolean` 值
157 |
158 | - 示例
159 |
160 | ```ts
161 | import { common } from 'dap-util'
162 | common.isObject('a') //false
163 | common.isObject({}) //true
164 | common.isObject({ name: 'a' }) //true
165 | ```
166 |
167 | ## copyText()
168 |
169 | 复制指定文本到剪切板
170 |
171 | - 类型
172 |
173 | ```ts
174 | copyText(text: string): Promise
175 | ```
176 |
177 | - 详细信息
178 |
179 | 提供一个入参,类型为 `string`,表示需要复制的文字内容
180 |
181 | 该方法内部调用的是 `navigator.clipboard.writeText` 方法,因此需要 `https` 支持
182 |
183 | - 示例
184 |
185 | ```ts
186 | import { common } from 'dap-util'
187 | common.copyText('hello')
188 | ```
189 |
190 | ## clone()
191 |
192 | 深度克隆
193 |
194 | - 类型
195 |
196 | ```ts
197 | clone(data: T): T
198 | ```
199 |
200 | - 详细信息
201 |
202 | 提供一个任意类型的入参,该方法会对该参数进行克隆,返回克隆的结果
203 |
204 | - 示例
205 |
206 | ```ts
207 | import { common } from 'dap-util'
208 | const a = {
209 | name: 'a',
210 | age: 14
211 | }
212 | const b = common.clone(a) // { name: 'a', age: 14 }
213 | ```
214 |
--------------------------------------------------------------------------------
/src/file.ts:
--------------------------------------------------------------------------------
1 | export type CompressOptionsType = {
2 | //压缩图片的宽,单位px,如果不设置默认为原图宽
3 | width?: number
4 | //压缩图片质量,默认为原图的0.8
5 | quality?: number
6 | //图片类型,jpeg或者webp,默认为jpeg
7 | mimeType?: 'jpeg' | 'webp'
8 | //压缩后的最大值,单位kb,默认为0表示不设置此值
9 | maxSize?: number
10 | //小于该大小的图片不进行压缩,单位kb,默认为0表示任何图片都要压缩
11 | minSize?: number
12 | }
13 |
14 | export type CompressResultType = {
15 | file?: File
16 | url?: string
17 | quality?: number
18 | width?: number
19 | height?: number
20 | }
21 |
22 | export const file = {
23 | /**
24 | * 根据文件获取本地可预览的图片路径
25 | * @param {Object} file
26 | */
27 | getImageUrl(file: File) {
28 | return window.URL.createObjectURL(file)
29 | },
30 |
31 | /**
32 | * 将JS的file对象转为BASE64位字符串,通过then方法回调,参数为base64字符串
33 | * @param {Object} file
34 | */
35 | dataFileToBase64(file: File) {
36 | return new Promise(resolve => {
37 | const reader = new FileReader()
38 | reader.readAsDataURL(file) // 读出 base64
39 | reader.onloadend = () => {
40 | // 图片的 base64 格式, 可以直接当成 img 的 src 属性值
41 | const dataURL = reader.result as string
42 | resolve(dataURL)
43 | }
44 | })
45 | },
46 |
47 | /**
48 | * 将base64位格式文件转换为file对象
49 | * @param {Object} base64String base64位格式字符串
50 | * @param {Object} fileName 转换后的文件名字,包含后缀
51 | */
52 | dataBase64toFile(base64String: string, fileName: string) {
53 | const arr: string[] = base64String.split(',')
54 | const mime = arr[0].match(/:(.*?);/)![1]
55 | const bstr = atob(arr[1])
56 | let n = bstr.length
57 | const u8arr = new Uint8Array(n)
58 | while (n--) {
59 | u8arr[n] = bstr.charCodeAt(n)
60 | }
61 | return new File([u8arr], fileName, {
62 | type: mime
63 | })
64 | },
65 |
66 | /**
67 | * 图片压缩方法
68 | * @param {*} file 需要压缩的图片File文件
69 | * @param {*} options 压缩参数
70 | */
71 | compressImage(file: File, options: CompressOptionsType) {
72 | //压缩图片的具体实现方法
73 | const createFile = (canvas: HTMLCanvasElement, fileName: string, quality: number): CompressResultType => {
74 | //压缩后图片的base64
75 | let url = canvas.toDataURL('image/' + (options.mimeType ?? 'jpeg'), quality)
76 | //压缩后图片的file类型文件
77 | let file = this.dataBase64toFile(url, fileName)
78 | //比最大尺寸大,继续压缩,此时会降低质量
79 | if (options.maxSize && options.maxSize > 0 && file.size > options.maxSize * 1024) {
80 | quality = quality <= 0 ? 0 : Number((quality - 0.01).toFixed(2))
81 | const res: CompressResultType = createFile(canvas, fileName, quality)
82 | url = res.url!
83 | file = res.file!
84 | quality = res.quality!
85 | }
86 | return {
87 | file,
88 | url,
89 | quality
90 | }
91 | }
92 | return new Promise((resolve, reject) => {
93 | //创建FileReader对象读取文件
94 | const reader = new FileReader()
95 | reader.readAsDataURL(file)
96 | reader.onload = () => {
97 | //获取文件的base64字符串
98 | const url = reader.result as string
99 | //创建图片对象
100 | const img = new Image()
101 | //设置图片链接地址
102 | img.src = url
103 | //图片加载完成事件触发
104 | img.onload = () => {
105 | //小于minSize的图片不压缩
106 | if (options.minSize && options.minSize > 0 && file.size <= options.minSize * 1024) {
107 | resolve({
108 | file,
109 | url,
110 | quality: 1,
111 | width: img.width,
112 | height: img.height
113 | })
114 | return
115 | }
116 | //创建画布
117 | const canvas = document.createElement('canvas')
118 | //创建2d上下文
119 | const context = canvas.getContext('2d')!
120 | //设置画布宽度
121 | canvas.width = options.width || img.width
122 | //设置画布高度
123 | canvas.height = options.width ? options.width / (img.width / img.height) : img.height
124 | //画图片
125 | context.drawImage(img, 0, 0, canvas.width, canvas.height)
126 | //设置压缩图片名称
127 | const index = file.name.lastIndexOf('.')
128 | const fileName = file.name.substring(0, index) + '.' + (options.mimeType ?? 'jpeg')
129 | //获取生成的文件和base64以及当前quality
130 | let res = createFile(canvas, fileName, options.quality ?? 0.8)
131 | resolve({
132 | ...res,
133 | width: canvas.width,
134 | height: canvas.height
135 | })
136 | }
137 | img.onerror = () => {
138 | reject(new Error('Failed to load image file'))
139 | }
140 | }
141 |
142 | reader.onerror = () => {
143 | reject(new Error('Failed to load image file'))
144 | }
145 | })
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/event.ts:
--------------------------------------------------------------------------------
1 | import { data as dataUtil } from './data'
2 |
3 | export type EventNameObjType = {
4 | eventName?: string
5 | guid?: string | number
6 | }
7 |
8 | export type EventObjType = {
9 | [key: string]:
10 | | {
11 | type: string
12 | fn: EventListenerOrEventListenerObject
13 | options?: AddEventListenerOptions
14 | }
15 | | undefined
16 | }
17 |
18 | //解析绑定事件名称字符串
19 | const parseEventName = (eventName: string) => {
20 | //先以空格划分
21 | const eventNames = eventName.split(/[\s]+/g)
22 | const result: EventNameObjType[] = []
23 | eventNames.forEach(name => {
24 | const arr = name.split('.')
25 | const obj: EventNameObjType = {
26 | eventName: arr[0]
27 | }
28 | if (arr.length > 1) {
29 | obj.guid = arr[1]
30 | }
31 | result.push(obj)
32 | })
33 | return result
34 | }
35 |
36 | //更新事件对象,移除空的元素
37 | const updateEvents = (events: EventObjType) => {
38 | const obj: EventObjType = {}
39 | const keys = Object.keys(events)
40 | keys.forEach(key => {
41 | if (events[key]) {
42 | obj[key] = events[key]
43 | }
44 | })
45 | return obj
46 | }
47 |
48 | //给元素添加单个事件
49 | const bindSingleListener = (el: HTMLElement | Window | Document, eventName: string, guid: string | number | undefined, fn: (e: Event) => void, options?: AddEventListenerOptions) => {
50 | //获取该元素上的事件对象events:{click.0:{type:'click',fn:fn}}
51 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events') || {}
52 | //如果没有设定guid
53 | if (!guid) {
54 | //从该元素上拿到记录的guid值
55 | guid = dataUtil.get(el, 'dap-event-guid') || 0
56 | //更新guid
57 | dataUtil.set(el, 'dap-event-guid', (guid as number) + 1)
58 | }
59 | //更改guid,结合事件名称作为存储的key值
60 | guid = eventName + '.' + guid
61 | //先判断是否已经含有同guid且同类型事件,有则移除
62 | if (events[guid] && events[guid]!.type == eventName) {
63 | el.removeEventListener(eventName, events[guid]!.fn, events[guid]!.options)
64 | }
65 | //添加事件
66 | el.addEventListener(eventName, fn, options)
67 | //添加到events对象里,并更新到节点上
68 | events[guid] = {
69 | type: eventName,
70 | fn: fn,
71 | options: options
72 | }
73 | dataUtil.set(el, 'dap-defined-events', events)
74 | }
75 |
76 | //移除元素的单个事件
77 | const unbindSingleListener = (el: HTMLElement | Window | Document, eventName: string, guid: string | number | undefined) => {
78 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events') || {}
79 | const keys = Object.keys(events)
80 | const length = keys.length
81 | for (let i = 0; i < length; i++) {
82 | const key = keys[i]
83 | if (events[key] && events[key].type == eventName) {
84 | //如果guid存在则移除该修饰符指定的事件,否则移除全部该类型事件
85 | if (guid) {
86 | if (key == eventName + '.' + guid) {
87 | el.removeEventListener(events[key].type, events[key].fn, events[key].options)
88 | events[key] = undefined
89 | }
90 | } else {
91 | el.removeEventListener(events[key].type, events[key].fn, events[key].options)
92 | events[key] = undefined
93 | }
94 | }
95 | }
96 | //更新events
97 | dataUtil.set(el, 'dap-defined-events', updateEvents(events))
98 | }
99 |
100 | export const event = {
101 | /**
102 | * 绑定事件
103 | * @param {Object} el 元素节点
104 | * @param {Object} eventName 事件名称
105 | * @param {Object} fn 函数
106 | * @param {Object} options 参数
107 | */
108 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions) {
109 | //解析eventName,获取事件数组以及guid标志
110 | const result = parseEventName(eventName)
111 | //批量添加事件
112 | result.forEach((res: EventNameObjType) => {
113 | bindSingleListener(el, res.eventName!, res.guid, fn.bind(el), options)
114 | })
115 | },
116 |
117 | /**
118 | * 事件解绑
119 | * @param {Object} el 元素节点
120 | * @param {Object} eventName 事件名称
121 | */
122 | off(el: HTMLElement | Window | Document, eventName?: string) {
123 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events')
124 | if (!events) {
125 | return
126 | }
127 | //事件名称不存在,则移除该元素的全部事件
128 | if (!eventName) {
129 | const keys = Object.keys(events)
130 | const length = keys.length
131 | for (let i = 0; i < length; i++) {
132 | const key = keys[i]
133 | el.removeEventListener(events[key]!.type, events[key]!.fn, events[key]!.options)
134 | }
135 | dataUtil.remove(el, 'dap-defined-events')
136 | dataUtil.remove(el, 'dap-event-guid')
137 | return
138 | }
139 | //解析eventName,获取事件数组以及guid标志
140 | const result = parseEventName(eventName)
141 | result.forEach((res: EventNameObjType) => {
142 | unbindSingleListener(el, res.eventName!, res.guid)
143 | })
144 | },
145 |
146 | /**
147 | * 获取绑定的所有事件
148 | * @param {*} el
149 | */
150 | get(el: HTMLElement | Window | Document) {
151 | const events: EventObjType = dataUtil.get(el, 'dap-defined-events')
152 | if (!events) {
153 | return
154 | }
155 | return events
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/lib/element.d.ts:
--------------------------------------------------------------------------------
1 | export type ScrollOptionsType = {
2 | el?: HTMLElement | string | Window;
3 | time?: number;
4 | number?: number;
5 | };
6 | export type PlacementType = {
7 | left: number;
8 | top: number;
9 | right: number;
10 | bottom: number;
11 | };
12 | export type scrollTopBottomResultType = {
13 | state: 'top' | 'bottom';
14 | target: HTMLElement | Window;
15 | };
16 | /**
17 | * element相关工具方法
18 | */
19 | export declare const element: {
20 | /**
21 | * 判断是否是Window对象
22 | * @param {Object} data 入参
23 | */
24 | isWindow(data: any): boolean;
25 | /**
26 | * 获取元素距离某个定位祖先元素左侧/顶部/底部/右侧的距离
27 | * @param {Object} el 元素
28 | * @param {Object} root 定位父元素或者祖先元素,未指定则为document.body
29 | */
30 | getElementPoint(el: HTMLElement, root?: HTMLElement): PlacementType;
31 | /**
32 | * 判断某个元素是否包含指定元素,包含相等关系和父子关系
33 | * @param {Object} parentNode 父元素或祖先元素
34 | * @param {Object} childNode 子元素
35 | */
36 | isContains(parentNode: HTMLElement, childNode: HTMLElement): boolean;
37 | /**
38 | * 判断某个元素是否是指定元素的父元素
39 | * @param {Object} parentNode 父元素
40 | * @param {Object} childNode 子元素
41 | */
42 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement): boolean;
43 | /**
44 | * 查找某个元素下指定选择器的子元素
45 | * @param {Object} el 元素
46 | * @param {Object} selector 支持多选择器,等同于querySelectorAll的参数
47 | */
48 | children(el: HTMLElement, selector?: string): HTMLElement[];
49 | /**
50 | * 查找某个元素下指定选择器的兄弟元素
51 | * @param {Object} el 元素
52 | * @param {Object} selector 取值等同于queryselectorAll的参数,支持多选择器
53 | */
54 | siblings(el: HTMLElement, selector?: string): HTMLElement[];
55 | /**
56 | * rem与px单位转换
57 | * @param {Object} num rem数值
58 | */
59 | rem2px(num: number): number;
60 | /**
61 | * rem与px单位转换
62 | * @param {Object} num px数值
63 | */
64 | px2rem(num: number): number;
65 | /**
66 | * 获取元素的内容宽度,内容宽度不包括border和padding
67 | * @param {Object} el 支持css选择器字符串,未指定则表示document.body
68 | */
69 | width(el?: HTMLElement | string): number;
70 | /**
71 | * 获取元素的内容高度,内容高度不包括border和padding
72 | * @param {Object} el 支持css选择器字符串 未指定则表示document.body
73 | */
74 | height(el?: HTMLElement | string): number;
75 | /**
76 | * 移除class
77 | * @param {Object} el 元素
78 | * @param {Object} className 支持多类,以空格划分
79 | */
80 | removeClass(el: HTMLElement, className: string): void;
81 | /**
82 | * 添加class
83 | * @param {Object} el 元素
84 | * @param {Object} className 支持多类,以空格划分
85 | */
86 | addClass(el: HTMLElement, className: string): void;
87 | /**
88 | * 判断指定元素是否含有指定类名
89 | * @param {Object} el 元素
90 | * @param {Object} className 支持多类,以空格划分
91 | */
92 | hasClass(el: HTMLElement, className: string): boolean;
93 | /**
94 | * 监听元素滚动到顶部或者底部
95 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
96 | * @param {Object} callback 回调函数
97 | */
98 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: scrollTopBottomResultType) => void): void;
99 | /**
100 | * 获取文档或元素的总宽度
101 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档
102 | */
103 | getScrollWidth(el?: HTMLElement | string): number;
104 | /**
105 | * 获取文档或者元素的总高度
106 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档
107 | */
108 | getScrollHeight(el?: HTMLElement | string): number;
109 | /**
110 | * 设置滚动条在Y轴上的距离
111 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动
112 | */
113 | setScrollTop(options: ScrollOptionsType): Promise;
114 | /**
115 | * 获取滚动条在Y轴上滚动的距离
116 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
117 | */
118 | getScrollTop(el?: HTMLElement | string | Window): number;
119 | /**
120 | * 获取滚动条在X轴上滚动的距离
121 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
122 | */
123 | getScrollLeft(el?: HTMLElement | string | Window): number;
124 | /**
125 | * 设置滚动条在X轴上的距离
126 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动
127 | */
128 | setScrollLeft(options: ScrollOptionsType): Promise;
129 | /**
130 | * 获取元素指定样式
131 | * @param {Object} el 元素
132 | * @param {Object} cssName 样式名称
133 | */
134 | getCssStyle(el: HTMLElement, cssName: string): string;
135 | /**
136 | * 判断字符串属于哪种选择器
137 | * @param {Object} selector
138 | */
139 | getCssSelector(selector: string): {
140 | type: "id" | "class" | "attribute" | "tag";
141 | value: string | {
142 | attributeName: string;
143 | attributeValue: string;
144 | };
145 | };
146 | /**
147 | * 获取元素距离可视窗口的位置
148 | * @param {Object} el 支持css选择器字符串 未指定则为document.body
149 | */
150 | getElementBounding(el?: HTMLElement | string): PlacementType;
151 | /**
152 | * 判断是否是元素
153 | * @param {Object} el
154 | */
155 | isElement(el: any): boolean;
156 | /**
157 | * 字符串转dom
158 | * @param {Object} html
159 | */
160 | string2dom(html: string): HTMLElement | HTMLElement[];
161 | };
162 |
--------------------------------------------------------------------------------
/src/platform.ts:
--------------------------------------------------------------------------------
1 | export type OSResultType = {
2 | Windows: boolean
3 | WindowsCPU?: 'x64' | 'x32'
4 | WindowsVersion?: 'win7' | 'win8' | 'win10'
5 | Mac: boolean
6 | MacVersion: string
7 | ios: boolean
8 | iosVersion: string
9 | Android: boolean
10 | AndroidVersion: string
11 | Linux: boolean
12 | HarmonyOS: boolean
13 | Ubuntu: boolean
14 | }
15 |
16 | //设备信息相关方法
17 | export const platform = {
18 | //设备语言类型
19 | language() {
20 | return window.navigator.language
21 | },
22 |
23 | /**
24 | * 获取设备类型
25 | */
26 | device() {
27 | const userAgent = window.navigator.userAgent
28 | return {
29 | PC: !userAgent.match(/AppleWebKit.*Mobile.*/),
30 | //是否移动终端
31 | Mobile: !!userAgent.match(/AppleWebKit.*Mobile.*/),
32 | //是否iPhone
33 | iPhone: userAgent.includes('iPhone'),
34 | //是否手机
35 | Phone: (userAgent.includes('Android') && /(?:Mobile)/.test(userAgent)) || userAgent.includes('iPhone') || /(?:Windows Phone)/.test(userAgent),
36 | //是否iPad
37 | iPad: userAgent.includes('iPad'),
38 | //是否平板电脑
39 | Tablet: userAgent.includes('iPad') || (userAgent.includes('Android') && !/(?:Mobile)/.test(userAgent)) || (userAgent.includes('Firefox') && /(?:Tablet)/.test(userAgent)),
40 | //windows手机
41 | WindowsPhone: /(?:Windows Phone)/.test(userAgent)
42 | }
43 | },
44 |
45 | /**
46 | * 获取浏览器类型
47 | */
48 | browser() {
49 | const userAgent = window.navigator.userAgent
50 | return {
51 | //是否edge浏览器
52 | Edge: !!userAgent.match(/Edg\/([\d.]+)/),
53 | //是否微信内置浏览器
54 | Weixin: userAgent.includes('MicroMessenger'),
55 | //是否QQ内置浏览器
56 | QQ: userAgent.includes('QQ'),
57 | //是否QQ浏览器
58 | QQBrowser: userAgent.includes('MQQBrowser'),
59 | //是否UC浏览器
60 | UC: userAgent.includes('UCBrowser'),
61 | //是否谷歌浏览器
62 | Chrome: userAgent.includes('Chrome'),
63 | //是否火狐浏览器
64 | Firefox: userAgent.includes('Firefox'),
65 | //是否搜狗浏览器
66 | Sougou: userAgent.toLocaleLowerCase().includes('se 2.x') || userAgent.toLocaleLowerCase().includes('metasr') || userAgent.toLocaleLowerCase().includes('sogou'),
67 | //是否safari浏览器
68 | Safari: userAgent.includes('Safari') && !userAgent.includes('Chrome')
69 | }
70 | },
71 |
72 | /**
73 | * 获取浏览器内核
74 | */
75 | browserKernel() {
76 | const userAgent = window.navigator.userAgent
77 | if (userAgent.includes('Presto')) {
78 | return 'opera'
79 | }
80 | if (userAgent.includes('AppleWebKit')) {
81 | return 'webkit'
82 | }
83 | if (userAgent.includes('Gecko') && !userAgent.includes('KHTML')) {
84 | return 'gecko'
85 | }
86 | },
87 |
88 | /**
89 | * 获取操作系统数据
90 | */
91 | os(): OSResultType {
92 | const userAgent = window.navigator.userAgent
93 | return {
94 | //是否Windows系统
95 | Windows: userAgent.includes('Windows'),
96 | //x64/x32
97 | WindowsCPU: (function () {
98 | if (userAgent.toLocaleLowerCase().includes('win64') || userAgent.toLocaleLowerCase().includes('wow64')) {
99 | return 'x64'
100 | } else if (userAgent.toLocaleLowerCase().includes('win32') || userAgent.toLocaleLowerCase().includes('wow32')) {
101 | return 'x32'
102 | }
103 | })(),
104 | //Windows版本
105 | WindowsVersion: (function () {
106 | if (userAgent.includes('Windows NT 6.1') || userAgent.includes('Windows 7')) {
107 | return 'win7'
108 | }
109 | if (userAgent.includes('Windows NT 6.3') || userAgent.includes('Windows NT 6.2') || userAgent.includes('Windows NT 8')) {
110 | return 'win8'
111 | }
112 | if (userAgent.includes('Windows NT 10') || userAgent.includes('Windows NT 6.4')) {
113 | return 'win10'
114 | }
115 | })(),
116 | //是否Mac
117 | Mac: userAgent.includes('Macintosh'),
118 | //Mac版本
119 | MacVersion: (function () {
120 | if (userAgent.includes('Macintosh')) {
121 | const matches = userAgent.match(/Mac OS X ([^\s]+)\)/)
122 | if (matches && matches[1]) {
123 | return matches[1].split(/_|\./).join('.')
124 | }
125 | }
126 | return ''
127 | })(),
128 | //是否ios系统
129 | ios: !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
130 | //ios系统版本
131 | iosVersion: (function () {
132 | if (!!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {
133 | const matches = userAgent.match(/CPU.+OS ([^\s]+) like Mac OS X/)
134 | if (matches && matches[1]) {
135 | return matches[1].split(/_|\./).join('.')
136 | }
137 | }
138 | return ''
139 | })(),
140 | //是否Android系统
141 | Android: userAgent.includes('Android'),
142 | //Android系统版本
143 | AndroidVersion: (function () {
144 | const matches = userAgent.match(/Android ([^\s]+);/)
145 | if (matches && matches[1]) {
146 | return matches[1].split(/_|\./).join('.')
147 | }
148 | return ''
149 | })(),
150 | //是否Linux系统
151 | Linux: userAgent.includes('Linux'),
152 | //是否鸿蒙系统
153 | HarmonyOS: userAgent.includes('HarmonyOS'),
154 | //是否Ubuntu系统
155 | Ubuntu: userAgent.includes('Ubuntu')
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/lib/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from './color';
2 | export * from './common';
3 | export * from './data';
4 | export * from './element';
5 | export * from './event';
6 | export * from './file';
7 | export * from './number';
8 | export * from './platform';
9 | export * from './speech';
10 | export * from './string';
11 | declare const _default: {
12 | number: {
13 | formatNumber(num: number): string;
14 | isNumber(num: any): boolean;
15 | add(...values: number[]): number;
16 | subtract(...values: number[]): number;
17 | mutiply(...values: number[]): number;
18 | divide(...values: number[]): number;
19 | };
20 | data: {
21 | remove(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key?: string): void;
22 | has(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key: string): boolean;
23 | get(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key?: string): T;
24 | set(el: import('./data').DataHTMLElement | import('./data').DataWindow | import('./data').DataDocument, key: string, value: any): void;
25 | };
26 | element: {
27 | isWindow(data: any): boolean;
28 | getElementPoint(el: HTMLElement, root?: HTMLElement): import('./element').PlacementType;
29 | isContains(parentNode: HTMLElement, childNode: HTMLElement): boolean;
30 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement): boolean;
31 | children(el: HTMLElement, selector?: string): HTMLElement[];
32 | siblings(el: HTMLElement, selector?: string): HTMLElement[];
33 | rem2px(num: number): number;
34 | px2rem(num: number): number;
35 | width(el?: HTMLElement | string): number;
36 | height(el?: HTMLElement | string): number;
37 | removeClass(el: HTMLElement, className: string): void;
38 | addClass(el: HTMLElement, className: string): void;
39 | hasClass(el: HTMLElement, className: string): boolean;
40 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: import('./element').scrollTopBottomResultType) => void): void;
41 | getScrollWidth(el?: HTMLElement | string): number;
42 | getScrollHeight(el?: HTMLElement | string): number;
43 | setScrollTop(options: import('./element').ScrollOptionsType): Promise;
44 | getScrollTop(el?: HTMLElement | string | Window): number;
45 | getScrollLeft(el?: HTMLElement | string | Window): number;
46 | setScrollLeft(options: import('./element').ScrollOptionsType): Promise;
47 | getCssStyle(el: HTMLElement, cssName: string): string;
48 | getCssSelector(selector: string): {
49 | type: "id" | "class" | "attribute" | "tag";
50 | value: string | {
51 | attributeName: string;
52 | attributeValue: string;
53 | };
54 | };
55 | getElementBounding(el?: HTMLElement | string): import('./element').PlacementType;
56 | isElement(el: any): boolean;
57 | string2dom(html: string): HTMLElement | HTMLElement[];
58 | };
59 | event: {
60 | on(el: HTMLElement | Window | Document, eventName: string, fn: (e: Event) => void, options?: AddEventListenerOptions): void;
61 | off(el: HTMLElement | Window | Document, eventName?: string): void;
62 | get(el: HTMLElement | Window | Document): import('./event').EventObjType | undefined;
63 | };
64 | common: {
65 | matchingText(text: string, param: import('./common').matchingParamType): boolean;
66 | getUrlParams(name: string): string | undefined;
67 | isEmptyObject(obj: any): boolean;
68 | equal(a: any, b: any): boolean;
69 | isObject(val: any): boolean;
70 | copyText(text: string): Promise;
71 | clone(data: T): T;
72 | };
73 | color: {
74 | rgb2hsv(rgb: number[]): number[];
75 | hsv2rgb(hsv: number[]): number[];
76 | rgb2hex(rgb: number[]): string;
77 | hex2rgb(hex: string): number[];
78 | };
79 | file: {
80 | getImageUrl(file: File): string;
81 | dataFileToBase64(file: File): Promise;
82 | dataBase64toFile(base64String: string, fileName: string): File;
83 | compressImage(file: File, options: import('./file').CompressOptionsType): Promise;
84 | };
85 | string: {
86 | insert(original: string, str: string, index: number): string;
87 | delete(original: string, index: number, num: number): string;
88 | replace(original: string, start: number, end: number, str: string): string;
89 | trim(str: string, global?: boolean): string;
90 | };
91 | platform: {
92 | language(): string;
93 | device(): {
94 | PC: boolean;
95 | Mobile: boolean;
96 | iPhone: boolean;
97 | Phone: boolean;
98 | iPad: boolean;
99 | Tablet: boolean;
100 | WindowsPhone: boolean;
101 | };
102 | browser(): {
103 | Edge: boolean;
104 | Weixin: boolean;
105 | QQ: boolean;
106 | QQBrowser: boolean;
107 | UC: boolean;
108 | Chrome: boolean;
109 | Firefox: boolean;
110 | Sougou: boolean;
111 | Safari: boolean;
112 | };
113 | browserKernel(): "opera" | "webkit" | "gecko" | undefined;
114 | os(): import('./platform').OSResultType;
115 | };
116 | speech: {
117 | start(text: string, options?: import('./speech').SpeechParamsType): void;
118 | stop(): void;
119 | pause(): void;
120 | resume(): void;
121 | };
122 | };
123 | export default _default;
124 |
--------------------------------------------------------------------------------
/src/common.ts:
--------------------------------------------------------------------------------
1 | export type matchingParamType = 'Chinese' | 'chinese' | 'email' | 'username' | 'int+' | 'int-' | 'int' | 'pos' | 'neg' | 'number' | 'phone' | 'idCard' | 'url' | 'IPv4' | 'hex' | 'rgb' | 'rgba' | 'QQ' | 'weixin' | 'plate'
2 |
3 | /**
4 | * 常用方法
5 | */
6 | export const common = {
7 | /**
8 | * 常用判断
9 | * @param {Object} text 要判断的字符串
10 | * @param {Object} param 判断的类型字符串
11 | */
12 | matchingText(text: string, param: matchingParamType) {
13 | //判断text是否为中文
14 | if (param == 'Chinese') {
15 | return /^[\u4e00-\u9fa5]+$/.test(text)
16 | }
17 | //判断text是否含有中文
18 | if (param == 'chinese') {
19 | return /[\u4e00-\u9fa5]/.test(text)
20 | }
21 | //判断text是否为邮箱
22 | if (param == 'email') {
23 | return /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(text)
24 | }
25 | //判断text是否为4-16位的用户名(字母数字下划线)
26 | if (param == 'username') {
27 | return /^[a-zA-Z0-9_]{4,16}$/.test(text)
28 | }
29 | //判断text是否为正整数
30 | if (param == 'int+') {
31 | return /^\d+$/.test(text)
32 | }
33 | //判断text是否为负整数
34 | if (param == 'int-') {
35 | return /^-\d+$/.test(text)
36 | }
37 | //判断text是否为整数
38 | if (param == 'int') {
39 | return /^-?\d+$/.test(text)
40 | }
41 | //判断text是否为正数
42 | if (param == 'pos') {
43 | return /^\d*\.?\d+$/.test(text)
44 | }
45 | //判断text是否为负数
46 | if (param == 'neg') {
47 | return /^-\d*\.?\d+$/.test(text)
48 | }
49 | //判断text是否为数字
50 | if (param == 'number') {
51 | return /^-?\d*\.?\d+$/.test(text)
52 | }
53 | //判断text是否为手机号
54 | if (param == 'phone') {
55 | return /^1[0-9]\d{9}$/.test(text)
56 | }
57 | //判断text是否为身份证号
58 | if (param == 'idCard') {
59 | return /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(text)
60 | }
61 | //判断text是否为网址
62 | if (param == 'url') {
63 | return /^(https?|ftp):\/\/(-\.)?([^\s\/?\.#-]+\.?)+(\/[^\s]*)?$/.test(text)
64 | }
65 | //判断是否为ip地址
66 | if (param == 'IPv4') {
67 | return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(text)
68 | }
69 | //判断是否为十六进制颜色
70 | if (param == 'hex') {
71 | return /^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(text)
72 | }
73 | //判断是否为rgb值
74 | if (param == 'rgb') {
75 | return /^rgb\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\)$/.test(text)
76 | }
77 | //判断是否为rgba值
78 | if (param == 'rgba') {
79 | return /^rgba\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(0?\.\d|1(\.0)?|0)\)$/.test(text)
80 | }
81 | //判断text是否为QQ号码
82 | if (param == 'QQ') {
83 | return /^[1-9][0-9]{4,10}$/.test(text)
84 | }
85 | //判断text是否为微信号,6至20位,以字母开头,字母,数字,减号,下划线
86 | if (param == 'weixin') {
87 | return /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/.test(text)
88 | }
89 | //判断text是否为车牌
90 | if (param == 'plate') {
91 | return /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$|^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[D|F]{1}[A-Z0-9]{5,6}$/.test(text)
92 | }
93 | return false
94 | },
95 |
96 | /**
97 | * 根据参数名获取地址栏参数值
98 | * @param {Object} name
99 | */
100 | getUrlParams(name: string) {
101 | const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')
102 | let search = window.location.search.substring(1)
103 | if (!search) {
104 | const arr = window.location.hash.split('?')
105 | if (arr.length == 2) {
106 | search = arr[1]
107 | }
108 | }
109 | let r = search.match(reg)
110 | if (r) {
111 | return decodeURIComponent(r[2])
112 | }
113 | },
114 |
115 | /**
116 | * 判断是否空对象
117 | * @param {Object} obj
118 | */
119 | isEmptyObject(obj: any) {
120 | return this.isObject(obj) && Object.keys(obj).length == 0
121 | },
122 |
123 | /**
124 | * 判断两个参数是否相等
125 | * @param {Object} a
126 | * @param {Object} b
127 | */
128 | equal(a: any, b: any) {
129 | if (typeof a !== typeof b) {
130 | return false
131 | }
132 | if (this.isObject(a) && this.isObject(b)) {
133 | const aProps = Object.getOwnPropertyNames(a)
134 | const bProps = Object.getOwnPropertyNames(b)
135 | if (aProps.length != bProps.length) {
136 | return false
137 | }
138 | const length = aProps.length
139 | let isEqual = true
140 | for (let i = 0; i < length; i++) {
141 | const propName = aProps[i]
142 | const propA = a[propName]
143 | const propB = b[propName]
144 | if (!this.equal(propA, propB)) {
145 | isEqual = false
146 | break
147 | }
148 | }
149 | return isEqual
150 | }
151 | return a === b
152 | },
153 |
154 | /**
155 | * 是否对象
156 | * @param {Object} val
157 | */
158 | isObject(val: any) {
159 | return typeof val === 'object' && !!val
160 | },
161 |
162 | /**
163 | * 文本复制
164 | * @param {Object} text
165 | */
166 | copyText(text: string) {
167 | if (!navigator.clipboard) {
168 | throw new Error("navigator.clipboard must be obtained in a secure environment, such as localhost, 127.0.0.1, or https, so the method won't work")
169 | }
170 | return navigator.clipboard.writeText(text)
171 | },
172 |
173 | /**
174 | * 深度克隆
175 | * @param {Object} data
176 | */
177 | clone(data: T) {
178 | if (this.isObject(data)) {
179 | if (Array.isArray(data)) {
180 | return data.map((item: any): any => {
181 | return this.clone(item)
182 | }) as T
183 | }
184 | const newData: any = {}
185 | for (let key in data) {
186 | newData[key] = this.clone(data[key])
187 | }
188 | return newData as T
189 | }
190 | return data as T
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vue.js:
--------------------------------------------------------------------------------
1 | import {
2 | BaseTransition,
3 | BaseTransitionPropsValidators,
4 | Comment,
5 | DeprecationTypes,
6 | EffectScope,
7 | ErrorCodes,
8 | ErrorTypeStrings,
9 | Fragment,
10 | KeepAlive,
11 | ReactiveEffect,
12 | Static,
13 | Suspense,
14 | Teleport,
15 | Text,
16 | TrackOpTypes,
17 | Transition,
18 | TransitionGroup,
19 | TriggerOpTypes,
20 | VueElement,
21 | assertNumber,
22 | callWithAsyncErrorHandling,
23 | callWithErrorHandling,
24 | camelize,
25 | capitalize,
26 | cloneVNode,
27 | compatUtils,
28 | compile,
29 | computed,
30 | createApp,
31 | createBaseVNode,
32 | createBlock,
33 | createCommentVNode,
34 | createElementBlock,
35 | createHydrationRenderer,
36 | createPropsRestProxy,
37 | createRenderer,
38 | createSSRApp,
39 | createSlots,
40 | createStaticVNode,
41 | createTextVNode,
42 | createVNode,
43 | customRef,
44 | defineAsyncComponent,
45 | defineComponent,
46 | defineCustomElement,
47 | defineEmits,
48 | defineExpose,
49 | defineModel,
50 | defineOptions,
51 | defineProps,
52 | defineSSRCustomElement,
53 | defineSlots,
54 | devtools,
55 | effect,
56 | effectScope,
57 | getCurrentInstance,
58 | getCurrentScope,
59 | getCurrentWatcher,
60 | getTransitionRawChildren,
61 | guardReactiveProps,
62 | h,
63 | handleError,
64 | hasInjectionContext,
65 | hydrate,
66 | hydrateOnIdle,
67 | hydrateOnInteraction,
68 | hydrateOnMediaQuery,
69 | hydrateOnVisible,
70 | initCustomFormatter,
71 | initDirectivesForSSR,
72 | inject,
73 | isMemoSame,
74 | isProxy,
75 | isReactive,
76 | isReadonly,
77 | isRef,
78 | isRuntimeOnly,
79 | isShallow,
80 | isVNode,
81 | markRaw,
82 | mergeDefaults,
83 | mergeModels,
84 | mergeProps,
85 | nextTick,
86 | normalizeClass,
87 | normalizeProps,
88 | normalizeStyle,
89 | onActivated,
90 | onBeforeMount,
91 | onBeforeUnmount,
92 | onBeforeUpdate,
93 | onDeactivated,
94 | onErrorCaptured,
95 | onMounted,
96 | onRenderTracked,
97 | onRenderTriggered,
98 | onScopeDispose,
99 | onServerPrefetch,
100 | onUnmounted,
101 | onUpdated,
102 | onWatcherCleanup,
103 | openBlock,
104 | popScopeId,
105 | provide,
106 | proxyRefs,
107 | pushScopeId,
108 | queuePostFlushCb,
109 | reactive,
110 | readonly,
111 | ref,
112 | registerRuntimeCompiler,
113 | render,
114 | renderList,
115 | renderSlot,
116 | resolveComponent,
117 | resolveDirective,
118 | resolveDynamicComponent,
119 | resolveFilter,
120 | resolveTransitionHooks,
121 | setBlockTracking,
122 | setDevtoolsHook,
123 | setTransitionHooks,
124 | shallowReactive,
125 | shallowReadonly,
126 | shallowRef,
127 | ssrContextKey,
128 | ssrUtils,
129 | stop,
130 | toDisplayString,
131 | toHandlerKey,
132 | toHandlers,
133 | toRaw,
134 | toRef,
135 | toRefs,
136 | toValue,
137 | transformVNodeArgs,
138 | triggerRef,
139 | unref,
140 | useAttrs,
141 | useCssModule,
142 | useCssVars,
143 | useHost,
144 | useId,
145 | useModel,
146 | useSSRContext,
147 | useShadowRoot,
148 | useSlots,
149 | useTemplateRef,
150 | useTransitionState,
151 | vModelCheckbox,
152 | vModelDynamic,
153 | vModelRadio,
154 | vModelSelect,
155 | vModelText,
156 | vShow,
157 | version,
158 | warn,
159 | watch,
160 | watchEffect,
161 | watchPostEffect,
162 | watchSyncEffect,
163 | withAsyncContext,
164 | withCtx,
165 | withDefaults,
166 | withDirectives,
167 | withKeys,
168 | withMemo,
169 | withModifiers,
170 | withScopeId
171 | } from "./chunk-LW4I4DCF.js";
172 | export {
173 | BaseTransition,
174 | BaseTransitionPropsValidators,
175 | Comment,
176 | DeprecationTypes,
177 | EffectScope,
178 | ErrorCodes,
179 | ErrorTypeStrings,
180 | Fragment,
181 | KeepAlive,
182 | ReactiveEffect,
183 | Static,
184 | Suspense,
185 | Teleport,
186 | Text,
187 | TrackOpTypes,
188 | Transition,
189 | TransitionGroup,
190 | TriggerOpTypes,
191 | VueElement,
192 | assertNumber,
193 | callWithAsyncErrorHandling,
194 | callWithErrorHandling,
195 | camelize,
196 | capitalize,
197 | cloneVNode,
198 | compatUtils,
199 | compile,
200 | computed,
201 | createApp,
202 | createBlock,
203 | createCommentVNode,
204 | createElementBlock,
205 | createBaseVNode as createElementVNode,
206 | createHydrationRenderer,
207 | createPropsRestProxy,
208 | createRenderer,
209 | createSSRApp,
210 | createSlots,
211 | createStaticVNode,
212 | createTextVNode,
213 | createVNode,
214 | customRef,
215 | defineAsyncComponent,
216 | defineComponent,
217 | defineCustomElement,
218 | defineEmits,
219 | defineExpose,
220 | defineModel,
221 | defineOptions,
222 | defineProps,
223 | defineSSRCustomElement,
224 | defineSlots,
225 | devtools,
226 | effect,
227 | effectScope,
228 | getCurrentInstance,
229 | getCurrentScope,
230 | getCurrentWatcher,
231 | getTransitionRawChildren,
232 | guardReactiveProps,
233 | h,
234 | handleError,
235 | hasInjectionContext,
236 | hydrate,
237 | hydrateOnIdle,
238 | hydrateOnInteraction,
239 | hydrateOnMediaQuery,
240 | hydrateOnVisible,
241 | initCustomFormatter,
242 | initDirectivesForSSR,
243 | inject,
244 | isMemoSame,
245 | isProxy,
246 | isReactive,
247 | isReadonly,
248 | isRef,
249 | isRuntimeOnly,
250 | isShallow,
251 | isVNode,
252 | markRaw,
253 | mergeDefaults,
254 | mergeModels,
255 | mergeProps,
256 | nextTick,
257 | normalizeClass,
258 | normalizeProps,
259 | normalizeStyle,
260 | onActivated,
261 | onBeforeMount,
262 | onBeforeUnmount,
263 | onBeforeUpdate,
264 | onDeactivated,
265 | onErrorCaptured,
266 | onMounted,
267 | onRenderTracked,
268 | onRenderTriggered,
269 | onScopeDispose,
270 | onServerPrefetch,
271 | onUnmounted,
272 | onUpdated,
273 | onWatcherCleanup,
274 | openBlock,
275 | popScopeId,
276 | provide,
277 | proxyRefs,
278 | pushScopeId,
279 | queuePostFlushCb,
280 | reactive,
281 | readonly,
282 | ref,
283 | registerRuntimeCompiler,
284 | render,
285 | renderList,
286 | renderSlot,
287 | resolveComponent,
288 | resolveDirective,
289 | resolveDynamicComponent,
290 | resolveFilter,
291 | resolveTransitionHooks,
292 | setBlockTracking,
293 | setDevtoolsHook,
294 | setTransitionHooks,
295 | shallowReactive,
296 | shallowReadonly,
297 | shallowRef,
298 | ssrContextKey,
299 | ssrUtils,
300 | stop,
301 | toDisplayString,
302 | toHandlerKey,
303 | toHandlers,
304 | toRaw,
305 | toRef,
306 | toRefs,
307 | toValue,
308 | transformVNodeArgs,
309 | triggerRef,
310 | unref,
311 | useAttrs,
312 | useCssModule,
313 | useCssVars,
314 | useHost,
315 | useId,
316 | useModel,
317 | useSSRContext,
318 | useShadowRoot,
319 | useSlots,
320 | useTemplateRef,
321 | useTransitionState,
322 | vModelCheckbox,
323 | vModelDynamic,
324 | vModelRadio,
325 | vModelSelect,
326 | vModelText,
327 | vShow,
328 | version,
329 | warn,
330 | watch,
331 | watchEffect,
332 | watchPostEffect,
333 | watchSyncEffect,
334 | withAsyncContext,
335 | withCtx,
336 | withDefaults,
337 | withDirectives,
338 | withKeys,
339 | withMemo,
340 | withModifiers,
341 | withScopeId
342 | };
343 | //# sourceMappingURL=vue.js.map
344 |
--------------------------------------------------------------------------------
/docs/guide/modules/element.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: element
3 | ---
4 |
5 | # element
6 |
7 | 元素模块
8 |
9 | ## isWindow()
10 |
11 | 判断是否 `Window` 对象
12 |
13 | - 类型
14 |
15 | ```ts
16 | isWindow(data: any): boolean
17 | ```
18 |
19 | - 详细信息
20 |
21 | 提供一个任意类型的入参,该方法会判断它是否为 `Window` 对象,返回 `boolean` 值
22 |
23 | - 示例
24 |
25 | ```ts
26 | import { element } from 'dap-util'
27 | element.isWindow(window) //true
28 | element.isWindow('a') //false
29 | ```
30 |
31 | ## getElementPoint()
32 |
33 | 获取元素距离指定祖先元素的位置
34 |
35 | - 类型
36 |
37 | ```ts
38 | getElementPoint(el: HTMLElement, root?: HTMLElement): PlacementType
39 | ```
40 |
41 | - 详细信息
42 |
43 | 第一个入参表示指定的元素,第二个入参表示目标元素,即指定的元素的定位父元素或者祖先元素,未设置则默认为 `document.body`
44 |
45 | 该方法会计算指定元素到目标元素之间的距离,返回值包含如下属性:
46 |
47 | - `top`:类型为 `number`,表示指定元素顶部到目标元素顶部的距离
48 | - `bottom`:类型为 `number`,表示指定元素底部到目标元素底部的距离
49 | - `left`:类型为 `number`,表示指定元素左侧到目标元素左侧的距离
50 | - `right`:类型为 `number`,表示指定元素右侧到目标元素右侧的距离
51 |
52 | - 示例
53 |
54 | ```ts
55 | import { element } from 'dap-util'
56 | //假定el是target的子元素
57 | const placement = element.getElementPoint(el, target)
58 | ```
59 |
60 | ## isContains()
61 |
62 | 判断某个元素是否包含另一个元素
63 |
64 | - 类型
65 |
66 | ```ts
67 | isContains(parentNode: HTMLElement, childNode: HTMLElement): boolean
68 | ```
69 |
70 | - 详细信息
71 |
72 | 第一个入参表示指定元素,第二个入参表示目标元素,该方法会判断目标元素是不是指定元素的子孙元素,即指定元素是包含目标元素的,如果二者是同一个元素,也视为包含关系,该方法返回 `boolean` 值
73 |
74 | - 示例
75 |
76 | ```ts
77 | import { element } from 'dap-util'
78 | const isContains = element.isContains(el, target)
79 | ```
80 |
81 | ## isParentNode()
82 |
83 | 判断某个元素是否另一个元素的父元素
84 |
85 | - 类型
86 |
87 | ```ts
88 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement): boolean
89 | ```
90 |
91 | - 详细信息
92 |
93 | 第一个入参表示指定的元素,第二个入参表示目标元素,该方法会判断指定元素是否目标元素的父元素,返回 `boolean` 值
94 |
95 | - 示例
96 |
97 | ```ts
98 | import { element } from 'dap-util'
99 | const isParentNode = element.isParentNode(el, target)
100 | ```
101 |
102 | ## children()
103 |
104 | 获取某个元素的子元素
105 |
106 | - 类型
107 |
108 | ```ts
109 | children(el: HTMLElement, selector?: string): HTMLElement[]
110 | ```
111 |
112 | - 详细信息
113 |
114 | 第一个入参表示指定的元素,第二个入参表示指定的选择器字符串,等同于 `querySelectorAll` 的参数,如果不设置则默认查询所有的子元素,如果设置了,则只查找符合选择器的子元素
115 |
116 | - 示例
117 |
118 | ```ts
119 | import { element } from 'dap-util'
120 | const children = element.children(document.body, '.a') // 查找body下的 class 是 a 的元素
121 | ```
122 |
123 | ## siblings()
124 |
125 | 获取某个元素的兄弟元素
126 |
127 | - 类型
128 |
129 | ```ts
130 | siblings(el: HTMLElement, selector?: string): HTMLElement[]
131 | ```
132 |
133 | - 详细信息
134 |
135 | 第一个入参表示指定的元素,第二个入参表示指定的选择器字符串,等同于 `querySelectorAll` 的参数,如果不设置则默认查询所有的兄弟元素,如果设置了,则只查找符合选择器的兄弟元素
136 |
137 | - 示例
138 |
139 | ```ts
140 | import { element } from 'dap-util'
141 | const elements = element.siblings(el, '.a') // 查找el的兄弟元素中 class 是 a 的元素
142 | ```
143 |
144 | ## rem2px()
145 |
146 | `rem` 的值转为 `px` 的值
147 |
148 | - 类型
149 |
150 | ```ts
151 | rem2px(num: number): number
152 | ```
153 |
154 | - 详细信息
155 |
156 | 提供一个入参,类型为 `number`,表示 `rem` 单位的值,该方法会返回一个值,表示 `px` 单位的值
157 |
158 | - 示例
159 |
160 | ```ts
161 | import { element } from 'dap-util'
162 | const pxValue = element.rem2px(1) //默认情况下1rem=16px,则输出16
163 | ```
164 |
165 | ## px2rem()
166 |
167 | `px` 的值转为 `rem` 的值
168 |
169 | - 类型
170 |
171 | ```ts
172 | px2rem(num: number): number
173 | ```
174 |
175 | - 详细信息
176 |
177 | 提供一个入参,类型为 `number`,表示 `px` 单位的值,该方法会返回一个值,表示 `rem` 单位的值
178 |
179 | - 示例
180 |
181 | ```ts
182 | import { element } from 'dap-util'
183 | const remValue = element.px2rem(32) //默认情况下1rem=16px,则输出2
184 | ```
185 |
186 | ## width()
187 |
188 | 获取元素的内容宽度
189 |
190 | - 类型
191 |
192 | ```ts
193 | width(el?: HTMLElement | string): number
194 | ```
195 |
196 | - 详细信息
197 |
198 | 提供一个入参,类型为 `HTMLElement | string`,表示指定的元素,支持 css 选择器字符串,如果不设置则默认为 `document.body`
199 |
200 | 该方法会返回指定元素的内容宽度,所谓内容宽度指的是不包括边框宽度和内边距大小
201 |
202 | - 示例
203 |
204 | ```ts
205 | import { element } from 'dap-util'
206 | const width = element.width(document.body) // 获取body的内容宽度
207 | ```
208 |
209 | ## height()
210 |
211 | 获取元素的内容高度
212 |
213 | - 类型
214 |
215 | ```ts
216 | height(el?: HTMLElement | string): number
217 | ```
218 |
219 | - 详细信息
220 |
221 | 提供一个入参,类型为 `HTMLElement | string`,表示指定的元素,支持 css 选择器字符串,如果不设置则默认为 `document.body`
222 |
223 | 该方法会返回指定元素的内容高度,所谓内容高度指的是不包括边框宽度和内边距大小
224 |
225 | - 示例
226 |
227 | ```ts
228 | import { element } from 'dap-util'
229 | const height = element.height(document.body) // 获取body的内容高度
230 | ```
231 |
232 | ## removeClass()
233 |
234 | 移除 `class`
235 |
236 | - 类型
237 |
238 | ```ts
239 | removeClass(el: HTMLElement, className: string): void
240 | ```
241 |
242 | - 详细信息
243 |
244 | 第一个入参表示需要移除 `class` 的元素,第二个参数表示移除的 `class` 值,支持移除多个,以空格划分
245 |
246 | - 示例
247 |
248 | ```ts
249 | import { element } from 'dap-util'
250 | element.removeClass(document.body, 'a b') //移除body上的a和b两个class
251 | ```
252 |
253 | ## addClass()
254 |
255 | 添加 `class`
256 |
257 | - 类型
258 |
259 | ```ts
260 | addClass(el: HTMLElement, className: string): void
261 | ```
262 |
263 | - 详细信息
264 |
265 | 第一个入参表示需要添加 `class` 的元素,第二个参数表示添加的 `class` 值,支持添加多个,以空格划分
266 |
267 | - 示例
268 |
269 | ```ts
270 | import { element } from 'dap-util'
271 | element.addClass(document.body, 'a b') //给body添加a和b两个class
272 | ```
273 |
274 | ## hasClass()
275 |
276 | 判断是否拥有指定的 `class`
277 |
278 | - 类型
279 |
280 | ```ts
281 | hasClass(el: HTMLElement, className: string): boolean
282 | ```
283 |
284 | - 详细信息
285 |
286 | 第一个入参表示需要判断的元素,第二个参数表示指定的 `class` 值,支持多个,以空格划分
287 |
288 | - 示例
289 |
290 | ```ts
291 | import { element } from 'dap-util'
292 | element.hasClass(document.body, 'a b') //判断body是否有a和b两个class
293 | ```
294 |
295 | ## scrollTopBottomTrigger()
296 |
297 | 监听元素滚动到顶部或者底部
298 |
299 | - 类型
300 |
301 | ```ts
302 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: scrollTopBottomResultType) => void): void
303 | ```
304 |
305 | - 详细信息
306 |
307 | 第一个入参表示监听的元素,支持 css 选择器字符串,如果未设置则默认为窗口滚动;第二个入参表示回调函数,该函数的回调参数包含 2 个属性:
308 |
309 | - `state`:取值为 `top` | `bottom`,表示滚动到顶部还是底部
310 | - `target`:滚动的元素
311 |
312 | 回调函数会在元素到达顶部或者底部时触发
313 |
314 | - 示例
315 |
316 | ```ts
317 | import { element } from 'dap-util'
318 | element.scrollTopBottomTrigger(document.body, options => {
319 | console.log(options.state) //通过state判断是滚动到顶部还是底部
320 | })
321 | ```
322 |
323 | ## getScrollWidth()
324 |
325 | 获取文档或元素的总宽度
326 |
327 | - 类型
328 |
329 | ```ts
330 | getScrollWidth(el?: HTMLElement | string): number
331 | ```
332 |
333 | - 详细信息
334 |
335 | 提供一个入参,表示指定的元素,支持 `css` 字符串选择器,如果不设置则默认为文档元素,该方法会返回该元素或者文档的总宽度,即如果有滚动条,则会包含滚动内容在内的宽度
336 |
337 | - 示例
338 |
339 | ```ts
340 | import { element } from 'dap-util'
341 | const width = element.getScrollWidth() //获取页面的总宽度
342 | ```
343 |
344 | ## getScrollHeight()
345 |
346 | 获取文档或元素的总高度
347 |
348 | - 类型
349 |
350 | ```ts
351 | getScrollHeight(el?: HTMLElement | string): number
352 | ```
353 |
354 | - 详细信息
355 |
356 | 提供一个入参,表示指定的元素,支持 `css` 字符串选择器,如果不设置则默认为文档元素,该方法会返回该元素或者文档的总高度,即如果有滚动条,则会包含滚动内容在内的高度
357 |
358 | - 示例
359 |
360 | ```ts
361 | import { element } from 'dap-util'
362 | const height = element.getScrollHeight() //获取页面的总高度
363 | ```
364 |
365 | ## setScrollTop()
366 |
367 | 设置滚动条在 Y 轴上的距离
368 |
369 | - 类型
370 |
371 | ```ts
372 | setScrollTop(options: ScrollOptionsType): Promise
373 | ```
374 |
375 | - 详细信息
376 |
377 | 提供一个入参,类型为 `ScrollOptionsType`,包含如下属性:
378 |
379 | - `el`:指定的元素,支持 `css` 选择器字符串,支持 `window`
380 | - `time`:滚动到指定距离的时间,单位 `ms`,不设置或者 `<=0` 则直接设置到指定距离
381 | - `number`:滚动到的指定距离,单位 `px`
382 |
383 | 该方法会将指定元素的垂直滚动条设置到指定的距离,如果设置了 `time`,则会有一个滚动的效果
384 |
385 | - 示例
386 |
387 | ```ts
388 | import { element } from 'dap-util'
389 | //将窗口的垂直滚动条滚动到120px的位置,用时500ms
390 | setScrollTop({
391 | el: window,
392 | time: 500,
393 | number: 120
394 | })
395 | ```
396 |
397 | ## getScrollTop()
398 |
399 | 获取滚动条在 Y 轴上滚动的距离
400 |
401 | - 类型
402 |
403 | ```ts
404 | getScrollTop(el?: HTMLElement | string | Window): number
405 | ```
406 |
407 | - 详细信息
408 |
409 | 提供一个入参,类型为 `HTMLElement | string | Window`,表示指定的元素,支持 `css` 选择器字符串,也支持 `window`,如果未指定,则默认为 `window`,该方法返回该元素垂直方向的滚动条滚动的距离,单位 `px`
410 |
411 | - 示例
412 |
413 | ```ts
414 | import { element } from 'dap-util'
415 | const scrollTop = element.getScrollTop()
416 | ```
417 |
418 | ## getScrollLeft()
419 |
420 | 获取滚动条在 X 轴上滚动的距离
421 |
422 | - 类型
423 |
424 | ```ts
425 | getScrollLeft(el?: HTMLElement | string | Window): number
426 | ```
427 |
428 | - 详细信息
429 |
430 | 提供一个入参,类型为 `HTMLElement | string | Window`,表示指定的元素,支持 `css` 选择器字符串,也支持 `window`,如果未指定,则默认为 `window`,该方法返回该元素水平方向的滚动条滚动的距离,单位 `px`
431 |
432 | - 示例
433 |
434 | ```ts
435 | import { element } from 'dap-util'
436 | const scrollLeft = element.getScrollLeft()
437 | ```
438 |
439 | ## setScrollLeft()
440 |
441 | 设置滚动条在 X 轴上的距离
442 |
443 | - 类型
444 |
445 | ```ts
446 | setScrollLeft(options: ScrollOptionsType): Promise
447 | ```
448 |
449 | - 详细信息
450 |
451 | 提供一个入参,类型为 `ScrollOptionsType`,包含如下属性:
452 |
453 | - `el`:指定的元素,支持 `css` 选择器字符串,支持 `window`
454 | - `time`:滚动到指定距离的时间,单位 `ms`,不设置或者 `<=0` 则直接设置到指定距离
455 | - `number`:滚动到的指定距离,单位 `px`
456 |
457 | 该方法会将指定元素的横向滚动条设置到指定的距离,如果设置了 `time`,则会有一个滚动的效果
458 |
459 | - 示例
460 |
461 | ```ts
462 | import { element } from 'dap-util'
463 | //将窗口的横向滚动条滚动到120px的位置,用时500ms
464 | setScrollLeft({
465 | el: window,
466 | time: 500,
467 | number: 120
468 | })
469 | ```
470 |
471 | ## getCssStyle()
472 |
473 | 获取元素指定样式
474 |
475 | - 类型
476 |
477 | ```ts
478 | getCssStyle(el: HTMLElement, cssName: string): string
479 | ```
480 |
481 | - 详细信息
482 |
483 | 第一个入参表示指定的元素,第二个入参表示 `css` 样式名称,该方法返回该 css 样式名称的值
484 |
485 | - 示例
486 |
487 | ```ts
488 | import { element } from 'dap-util'
489 | const color = element.getCssStyle(el, 'color') //获取el元素的color值
490 | ```
491 |
492 | ## getCssSelector()
493 |
494 | 判断字符串属于哪种 css 选择器
495 |
496 | - 类型
497 |
498 | ```ts
499 | getCssSelector(selector: string): {
500 | type: "id" | "class" | "attribute" | "tag";
501 | value: string | {
502 | attributeName: string;
503 | attributeValue: string;
504 | };
505 | }
506 | ```
507 |
508 | - 详细信息
509 |
510 | 提供一个入参,类型为 `string`,表示需要判断的 `css` 选择器字符串
511 |
512 | 该方法返回的值包含 2 个属性:
513 |
514 | - `type`:选择器的类型,可取值为 `id` `class` `attribute` `tag`,分别表示 ID 选择器、类名选择器、属性选择器、标签选择器
515 | - `value`:选择器的值,如果是属性选择器,可存在属性值的情况下,该属性包含 attributeName 和 attributeValue 两个字段,分别表示属性名称和属性值,如 `[data-id="a"]`,其他情况下返回值都是字符串
516 |
517 | - 示例
518 |
519 | ```ts
520 | import { element } from 'dap-util'
521 | const val = element.getCssSelector('#a') //a
522 | const val = element.getCssSelector('.red') //red
523 | const val = element.getCssSelector('[data-id="a"') //{ attributeName: "data-id", attributeValue: "a" }
524 | ```
525 |
526 | ## getElementBounding()
527 |
528 | 获取元素距离可视窗口的位置
529 |
530 | - 类型
531 |
532 | ```ts
533 | getElementBounding(el?: HTMLElement | string): PlacementType
534 | ```
535 |
536 | - 详细信息
537 |
538 | 提供一个入参,表示指定的元素,该方法会获取指定元素到可视窗口之间的距离,返回值包含如下属性:
539 |
540 | - `top`:类型为 `number`,表示指定元素顶部到可视窗口顶部的距离
541 | - `bottom`:类型为 `number`,表示指定元素底部到可视窗口底部的距离
542 | - `left`:类型为 `number`,表示指定元素左侧到可视窗口左侧的距离
543 | - `right`:类型为 `number`,表示指定元素右侧到可视窗口右侧的距离
544 |
545 | - 示例
546 |
547 | ```ts
548 | import { element } from 'dap-util'
549 | const placement = element.getElementBounding(el)
550 | ```
551 |
552 | ## isElement()
553 |
554 | 判断是否是元素
555 |
556 | - 类型
557 |
558 | ```ts
559 | isElement(el: any): boolean
560 | ```
561 |
562 | - 详细信息
563 |
564 | 提供一个任意类型的入参,该方法会判断入参是否为元素类型,所谓元素类型指的是类型为 `Node` 且 `nodeType` 为
565 |
566 | - 示例
567 |
568 | ```ts
569 | import { element } from 'dap-util'
570 | element.isElement('a') //false
571 | element.isElement(document.body) //true
572 | ```
573 |
574 | ## string2dom()
575 |
576 | 字符串转 dom
577 |
578 | - 类型
579 |
580 | ```ts
581 | string2dom(html: string): HTMLElement | HTMLElement[]
582 | ```
583 |
584 | - 详细信息
585 |
586 | 提供一个入参,类型为 `string`,表示需要转换的字符串,该方法返回一个元素或者元素数组
587 |
588 | - 示例
589 |
590 | ```ts
591 | import { element } from 'dap-util'
592 | const dom = element.string2dom('hello
')
593 | ```
594 |
--------------------------------------------------------------------------------
/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js:
--------------------------------------------------------------------------------
1 | import {
2 | DefaultMagicKeysAliasMap,
3 | StorageSerializers,
4 | TransitionPresets,
5 | assert,
6 | breakpointsAntDesign,
7 | breakpointsBootstrapV5,
8 | breakpointsElement,
9 | breakpointsMasterCss,
10 | breakpointsPrimeFlex,
11 | breakpointsQuasar,
12 | breakpointsSematic,
13 | breakpointsTailwind,
14 | breakpointsVuetify,
15 | breakpointsVuetifyV2,
16 | breakpointsVuetifyV3,
17 | bypassFilter,
18 | camelize,
19 | clamp,
20 | cloneFnJSON,
21 | computedAsync,
22 | computedEager,
23 | computedInject,
24 | computedWithControl,
25 | containsProp,
26 | controlledRef,
27 | createEventHook,
28 | createFetch,
29 | createFilterWrapper,
30 | createGlobalState,
31 | createInjectionState,
32 | createReusableTemplate,
33 | createSharedComposable,
34 | createSingletonPromise,
35 | createTemplatePromise,
36 | createUnrefFn,
37 | customStorageEventName,
38 | debounceFilter,
39 | defaultDocument,
40 | defaultLocation,
41 | defaultNavigator,
42 | defaultWindow,
43 | directiveHooks,
44 | executeTransition,
45 | extendRef,
46 | formatDate,
47 | formatTimeAgo,
48 | get,
49 | getLifeCycleTarget,
50 | getSSRHandler,
51 | hasOwn,
52 | hyphenate,
53 | identity,
54 | increaseWithUnit,
55 | injectLocal,
56 | invoke,
57 | isClient,
58 | isDef,
59 | isDefined,
60 | isIOS,
61 | isObject,
62 | isWorker,
63 | makeDestructurable,
64 | mapGamepadToXbox360Controller,
65 | noop,
66 | normalizeDate,
67 | notNullish,
68 | now,
69 | objectEntries,
70 | objectOmit,
71 | objectPick,
72 | onClickOutside,
73 | onKeyDown,
74 | onKeyPressed,
75 | onKeyStroke,
76 | onKeyUp,
77 | onLongPress,
78 | onStartTyping,
79 | pausableFilter,
80 | promiseTimeout,
81 | provideLocal,
82 | rand,
83 | reactify,
84 | reactifyObject,
85 | reactiveComputed,
86 | reactiveOmit,
87 | reactivePick,
88 | refAutoReset,
89 | refDebounced,
90 | refDefault,
91 | refThrottled,
92 | refWithControl,
93 | resolveRef,
94 | resolveUnref,
95 | set,
96 | setSSRHandler,
97 | syncRef,
98 | syncRefs,
99 | templateRef,
100 | throttleFilter,
101 | timestamp,
102 | toReactive,
103 | toRef,
104 | toRefs,
105 | toValue,
106 | tryOnBeforeMount,
107 | tryOnBeforeUnmount,
108 | tryOnMounted,
109 | tryOnScopeDispose,
110 | tryOnUnmounted,
111 | unrefElement,
112 | until,
113 | useActiveElement,
114 | useAnimate,
115 | useArrayDifference,
116 | useArrayEvery,
117 | useArrayFilter,
118 | useArrayFind,
119 | useArrayFindIndex,
120 | useArrayFindLast,
121 | useArrayIncludes,
122 | useArrayJoin,
123 | useArrayMap,
124 | useArrayReduce,
125 | useArraySome,
126 | useArrayUnique,
127 | useAsyncQueue,
128 | useAsyncState,
129 | useBase64,
130 | useBattery,
131 | useBluetooth,
132 | useBreakpoints,
133 | useBroadcastChannel,
134 | useBrowserLocation,
135 | useCached,
136 | useClipboard,
137 | useClipboardItems,
138 | useCloned,
139 | useColorMode,
140 | useConfirmDialog,
141 | useCounter,
142 | useCssVar,
143 | useCurrentElement,
144 | useCycleList,
145 | useDark,
146 | useDateFormat,
147 | useDebounceFn,
148 | useDebouncedRefHistory,
149 | useDeviceMotion,
150 | useDeviceOrientation,
151 | useDevicePixelRatio,
152 | useDevicesList,
153 | useDisplayMedia,
154 | useDocumentVisibility,
155 | useDraggable,
156 | useDropZone,
157 | useElementBounding,
158 | useElementByPoint,
159 | useElementHover,
160 | useElementSize,
161 | useElementVisibility,
162 | useEventBus,
163 | useEventListener,
164 | useEventSource,
165 | useEyeDropper,
166 | useFavicon,
167 | useFetch,
168 | useFileDialog,
169 | useFileSystemAccess,
170 | useFocus,
171 | useFocusWithin,
172 | useFps,
173 | useFullscreen,
174 | useGamepad,
175 | useGeolocation,
176 | useIdle,
177 | useImage,
178 | useInfiniteScroll,
179 | useIntersectionObserver,
180 | useInterval,
181 | useIntervalFn,
182 | useKeyModifier,
183 | useLastChanged,
184 | useLocalStorage,
185 | useMagicKeys,
186 | useManualRefHistory,
187 | useMediaControls,
188 | useMediaQuery,
189 | useMemoize,
190 | useMemory,
191 | useMounted,
192 | useMouse,
193 | useMouseInElement,
194 | useMousePressed,
195 | useMutationObserver,
196 | useNavigatorLanguage,
197 | useNetwork,
198 | useNow,
199 | useObjectUrl,
200 | useOffsetPagination,
201 | useOnline,
202 | usePageLeave,
203 | useParallax,
204 | useParentElement,
205 | usePerformanceObserver,
206 | usePermission,
207 | usePointer,
208 | usePointerLock,
209 | usePointerSwipe,
210 | usePreferredColorScheme,
211 | usePreferredContrast,
212 | usePreferredDark,
213 | usePreferredLanguages,
214 | usePreferredReducedMotion,
215 | usePrevious,
216 | useRafFn,
217 | useRefHistory,
218 | useResizeObserver,
219 | useScreenOrientation,
220 | useScreenSafeArea,
221 | useScriptTag,
222 | useScroll,
223 | useScrollLock,
224 | useSessionStorage,
225 | useShare,
226 | useSorted,
227 | useSpeechRecognition,
228 | useSpeechSynthesis,
229 | useStepper,
230 | useStorage,
231 | useStorageAsync,
232 | useStyleTag,
233 | useSupported,
234 | useSwipe,
235 | useTemplateRefsList,
236 | useTextDirection,
237 | useTextSelection,
238 | useTextareaAutosize,
239 | useThrottleFn,
240 | useThrottledRefHistory,
241 | useTimeAgo,
242 | useTimeout,
243 | useTimeoutFn,
244 | useTimeoutPoll,
245 | useTimestamp,
246 | useTitle,
247 | useToNumber,
248 | useToString,
249 | useToggle,
250 | useTransition,
251 | useUrlSearchParams,
252 | useUserMedia,
253 | useVModel,
254 | useVModels,
255 | useVibrate,
256 | useVirtualList,
257 | useWakeLock,
258 | useWebNotification,
259 | useWebSocket,
260 | useWebWorker,
261 | useWebWorkerFn,
262 | useWindowFocus,
263 | useWindowScroll,
264 | useWindowSize,
265 | watchArray,
266 | watchAtMost,
267 | watchDebounced,
268 | watchDeep,
269 | watchIgnorable,
270 | watchImmediate,
271 | watchOnce,
272 | watchPausable,
273 | watchThrottled,
274 | watchTriggerable,
275 | watchWithFilter,
276 | whenever
277 | } from "./chunk-YJ6QP2VR.js";
278 | import "./chunk-LW4I4DCF.js";
279 | export {
280 | DefaultMagicKeysAliasMap,
281 | StorageSerializers,
282 | TransitionPresets,
283 | assert,
284 | computedAsync as asyncComputed,
285 | refAutoReset as autoResetRef,
286 | breakpointsAntDesign,
287 | breakpointsBootstrapV5,
288 | breakpointsElement,
289 | breakpointsMasterCss,
290 | breakpointsPrimeFlex,
291 | breakpointsQuasar,
292 | breakpointsSematic,
293 | breakpointsTailwind,
294 | breakpointsVuetify,
295 | breakpointsVuetifyV2,
296 | breakpointsVuetifyV3,
297 | bypassFilter,
298 | camelize,
299 | clamp,
300 | cloneFnJSON,
301 | computedAsync,
302 | computedEager,
303 | computedInject,
304 | computedWithControl,
305 | containsProp,
306 | computedWithControl as controlledComputed,
307 | controlledRef,
308 | createEventHook,
309 | createFetch,
310 | createFilterWrapper,
311 | createGlobalState,
312 | createInjectionState,
313 | reactify as createReactiveFn,
314 | createReusableTemplate,
315 | createSharedComposable,
316 | createSingletonPromise,
317 | createTemplatePromise,
318 | createUnrefFn,
319 | customStorageEventName,
320 | debounceFilter,
321 | refDebounced as debouncedRef,
322 | watchDebounced as debouncedWatch,
323 | defaultDocument,
324 | defaultLocation,
325 | defaultNavigator,
326 | defaultWindow,
327 | directiveHooks,
328 | computedEager as eagerComputed,
329 | executeTransition,
330 | extendRef,
331 | formatDate,
332 | formatTimeAgo,
333 | get,
334 | getLifeCycleTarget,
335 | getSSRHandler,
336 | hasOwn,
337 | hyphenate,
338 | identity,
339 | watchIgnorable as ignorableWatch,
340 | increaseWithUnit,
341 | injectLocal,
342 | invoke,
343 | isClient,
344 | isDef,
345 | isDefined,
346 | isIOS,
347 | isObject,
348 | isWorker,
349 | makeDestructurable,
350 | mapGamepadToXbox360Controller,
351 | noop,
352 | normalizeDate,
353 | notNullish,
354 | now,
355 | objectEntries,
356 | objectOmit,
357 | objectPick,
358 | onClickOutside,
359 | onKeyDown,
360 | onKeyPressed,
361 | onKeyStroke,
362 | onKeyUp,
363 | onLongPress,
364 | onStartTyping,
365 | pausableFilter,
366 | watchPausable as pausableWatch,
367 | promiseTimeout,
368 | provideLocal,
369 | rand,
370 | reactify,
371 | reactifyObject,
372 | reactiveComputed,
373 | reactiveOmit,
374 | reactivePick,
375 | refAutoReset,
376 | refDebounced,
377 | refDefault,
378 | refThrottled,
379 | refWithControl,
380 | resolveRef,
381 | resolveUnref,
382 | set,
383 | setSSRHandler,
384 | syncRef,
385 | syncRefs,
386 | templateRef,
387 | throttleFilter,
388 | refThrottled as throttledRef,
389 | watchThrottled as throttledWatch,
390 | timestamp,
391 | toReactive,
392 | toRef,
393 | toRefs,
394 | toValue,
395 | tryOnBeforeMount,
396 | tryOnBeforeUnmount,
397 | tryOnMounted,
398 | tryOnScopeDispose,
399 | tryOnUnmounted,
400 | unrefElement,
401 | until,
402 | useActiveElement,
403 | useAnimate,
404 | useArrayDifference,
405 | useArrayEvery,
406 | useArrayFilter,
407 | useArrayFind,
408 | useArrayFindIndex,
409 | useArrayFindLast,
410 | useArrayIncludes,
411 | useArrayJoin,
412 | useArrayMap,
413 | useArrayReduce,
414 | useArraySome,
415 | useArrayUnique,
416 | useAsyncQueue,
417 | useAsyncState,
418 | useBase64,
419 | useBattery,
420 | useBluetooth,
421 | useBreakpoints,
422 | useBroadcastChannel,
423 | useBrowserLocation,
424 | useCached,
425 | useClipboard,
426 | useClipboardItems,
427 | useCloned,
428 | useColorMode,
429 | useConfirmDialog,
430 | useCounter,
431 | useCssVar,
432 | useCurrentElement,
433 | useCycleList,
434 | useDark,
435 | useDateFormat,
436 | refDebounced as useDebounce,
437 | useDebounceFn,
438 | useDebouncedRefHistory,
439 | useDeviceMotion,
440 | useDeviceOrientation,
441 | useDevicePixelRatio,
442 | useDevicesList,
443 | useDisplayMedia,
444 | useDocumentVisibility,
445 | useDraggable,
446 | useDropZone,
447 | useElementBounding,
448 | useElementByPoint,
449 | useElementHover,
450 | useElementSize,
451 | useElementVisibility,
452 | useEventBus,
453 | useEventListener,
454 | useEventSource,
455 | useEyeDropper,
456 | useFavicon,
457 | useFetch,
458 | useFileDialog,
459 | useFileSystemAccess,
460 | useFocus,
461 | useFocusWithin,
462 | useFps,
463 | useFullscreen,
464 | useGamepad,
465 | useGeolocation,
466 | useIdle,
467 | useImage,
468 | useInfiniteScroll,
469 | useIntersectionObserver,
470 | useInterval,
471 | useIntervalFn,
472 | useKeyModifier,
473 | useLastChanged,
474 | useLocalStorage,
475 | useMagicKeys,
476 | useManualRefHistory,
477 | useMediaControls,
478 | useMediaQuery,
479 | useMemoize,
480 | useMemory,
481 | useMounted,
482 | useMouse,
483 | useMouseInElement,
484 | useMousePressed,
485 | useMutationObserver,
486 | useNavigatorLanguage,
487 | useNetwork,
488 | useNow,
489 | useObjectUrl,
490 | useOffsetPagination,
491 | useOnline,
492 | usePageLeave,
493 | useParallax,
494 | useParentElement,
495 | usePerformanceObserver,
496 | usePermission,
497 | usePointer,
498 | usePointerLock,
499 | usePointerSwipe,
500 | usePreferredColorScheme,
501 | usePreferredContrast,
502 | usePreferredDark,
503 | usePreferredLanguages,
504 | usePreferredReducedMotion,
505 | usePrevious,
506 | useRafFn,
507 | useRefHistory,
508 | useResizeObserver,
509 | useScreenOrientation,
510 | useScreenSafeArea,
511 | useScriptTag,
512 | useScroll,
513 | useScrollLock,
514 | useSessionStorage,
515 | useShare,
516 | useSorted,
517 | useSpeechRecognition,
518 | useSpeechSynthesis,
519 | useStepper,
520 | useStorage,
521 | useStorageAsync,
522 | useStyleTag,
523 | useSupported,
524 | useSwipe,
525 | useTemplateRefsList,
526 | useTextDirection,
527 | useTextSelection,
528 | useTextareaAutosize,
529 | refThrottled as useThrottle,
530 | useThrottleFn,
531 | useThrottledRefHistory,
532 | useTimeAgo,
533 | useTimeout,
534 | useTimeoutFn,
535 | useTimeoutPoll,
536 | useTimestamp,
537 | useTitle,
538 | useToNumber,
539 | useToString,
540 | useToggle,
541 | useTransition,
542 | useUrlSearchParams,
543 | useUserMedia,
544 | useVModel,
545 | useVModels,
546 | useVibrate,
547 | useVirtualList,
548 | useWakeLock,
549 | useWebNotification,
550 | useWebSocket,
551 | useWebWorker,
552 | useWebWorkerFn,
553 | useWindowFocus,
554 | useWindowScroll,
555 | useWindowSize,
556 | watchArray,
557 | watchAtMost,
558 | watchDebounced,
559 | watchDeep,
560 | watchIgnorable,
561 | watchImmediate,
562 | watchOnce,
563 | watchPausable,
564 | watchThrottled,
565 | watchTriggerable,
566 | watchWithFilter,
567 | whenever
568 | };
569 | //# sourceMappingURL=vitepress___@vueuse_core.js.map
570 |
--------------------------------------------------------------------------------
/lib/dap-util.umd.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Dap={})}(this,(function(e){"use strict";const t={matchingText:(e,t)=>"Chinese"==t?/^[\u4e00-\u9fa5]+$/.test(e):"chinese"==t?/[\u4e00-\u9fa5]/.test(e):"email"==t?/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(e):"username"==t?/^[a-zA-Z0-9_]{4,16}$/.test(e):"int+"==t?/^\d+$/.test(e):"int-"==t?/^-\d+$/.test(e):"int"==t?/^-?\d+$/.test(e):"pos"==t?/^\d*\.?\d+$/.test(e):"neg"==t?/^-\d*\.?\d+$/.test(e):"number"==t?/^-?\d*\.?\d+$/.test(e):"phone"==t?/^1[0-9]\d{9}$/.test(e):"idCard"==t?/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(e):"url"==t?/^(https?|ftp):\/\/(-\.)?([^\s\/?\.#-]+\.?)+(\/[^\s]*)?$/.test(e):"IPv4"==t?/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(e):"hex"==t?/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(e):"rgb"==t?/^rgb\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\)$/.test(e):"rgba"==t?/^rgba\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(0?\.\d|1(\.0)?|0)\)$/.test(e):"QQ"==t?/^[1-9][0-9]{4,10}$/.test(e):"weixin"==t?/^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/.test(e):"plate"==t&&/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$|^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[D|F]{1}[A-Z0-9]{5,6}$/.test(e),getUrlParams(e){const t=new RegExp("(^|&)"+e+"=([^&]*)(&|$)");let n=window.location.search.substring(1);if(!n){const e=window.location.hash.split("?");2==e.length&&(n=e[1])}let o=n.match(t);if(o)return decodeURIComponent(o[2])},isEmptyObject(e){return this.isObject(e)&&0==Object.keys(e).length},equal(e,t){if(typeof e!=typeof t)return!1;if(this.isObject(e)&&this.isObject(t)){const n=Object.getOwnPropertyNames(e),o=Object.getOwnPropertyNames(t);if(n.length!=o.length)return!1;const r=n.length;let s=!0;for(let i=0;i"object"==typeof e&&!!e,copyText(e){if(!navigator.clipboard)throw new Error("navigator.clipboard must be obtained in a secure environment, such as localhost, 127.0.0.1, or https, so the method won't work");return navigator.clipboard.writeText(e)},clone(e){if(this.isObject(e)){if(Array.isArray(e))return e.map((e=>this.clone(e)));const t={};for(let n in e)t[n]=this.clone(e[n]);return t}return e}},n={rgb2hsv(e){if(3!=e.length)throw new TypeError("Invalid argument");let t=0,n=0,o=0,r=e[0]>=255?255:e[0],s=e[1]>=255?255:e[1],i=e[2]>=255?255:e[2];r=r<=0?0:r,s=s<=0?0:s,i=i<=0?0:i;let l=Math.max(r,s,i),d=Math.min(r,s,i);return o=l/255,n=0===l?0:1-d/l,l===d?t=0:l===r&&s>=i?t=(s-i)/(l-d)*60+0:l===r&&s=360||e[0]<=0?0:e[0],n=e[1]>=100?100:e[1];n=n<=0?0:n;let o=e[2]>=100?100:e[2];o=o<=0?0:o,n/=100,o/=100;let r=0,s=0,i=0,l=parseInt(t/60%6+""),d=t/60-l,c=o*(1-n),a=o*(1-d*n),u=o*(1-(1-d)*n);switch(l){case 0:r=o,s=u,i=c;break;case 1:r=a,s=o,i=c;break;case 2:r=c,s=o,i=u;break;case 3:r=c,s=a,i=o;break;case 4:r=u,s=c,i=o;break;case 5:r=o,s=c,i=a}return r=parseInt(255*r+""),s=parseInt(255*s+""),i=parseInt(255*i+""),[r,s,i]},rgb2hex(e){if(3!=e.length)throw new TypeError("Invalid argument");return"#"+((1<<24)+(e[0]<<16)+(e[1]<<8)+e[2]).toString(16).slice(1)},hex2rgb(e){let n=e.toLowerCase();if(!t.matchingText(n,"hex"))throw new TypeError("The argument must be a hexadecimal color value");if(4===n.length){let e="#";for(let t=1;t<4;t+=1)e+=n.slice(t,t+1).concat(n.slice(t,t+1));n=e}let o=[];for(let t=1;t<7;t+=2)o.push(parseInt("0x"+n.slice(t,t+2)));return o}},o="_dap-datas",r={remove(e,t){const n=e[o]||{};t?(delete n[t],e[o]=n):e[o]={}},has:(e,t)=>(e[o]||{}).hasOwnProperty(t),get(e,t){const n=e[o]||{};return t?n[t]:n},set(e,t,n){const r=e[o]||{};r[t]=n,e[o]=r}},s={insert(e,t,n){if(n<0)throw new Error("The third argument cannot be less than 0");return e.substring(0,n)+t+e.substring(n,e.length)},delete(e,t,n){if(t<0)throw new Error("The second argument cannot be less than 0");if(n<0)throw new Error("The third argument cannot be less than 0");return e.substring(0,t)+e.substring(t+n,e.length)},replace(e,t,n,o){if(t<0)throw new Error("The second argument cannot be less than 0");if(n<0)throw new Error("The third argument cannot be less than 0");return e.substring(0,t)+o+e.substring(n,e.length)},trim(e,t){let n=e.replace(/(^\s+)|(\s+$)/g,"");return t&&(n=n.replace(/\s/g,"")),n}},i={formatNumber(e){return this.isNumber(e)?e.toString().replace(/(\d)(?=(?:\d{3})+$)/g,"$1,"):e.toString()},isNumber:e=>"number"==typeof e&&!isNaN(e),add:(...e)=>e.reduce(((e,t)=>{let n=0,o=0,r=0;try{n=e.toString().split(".")[1].length}catch(s){}try{o=t.toString().split(".")[1].length}catch(s){}return r=Math.pow(10,Math.max(n,o)),(e*r+t*r)/r})),subtract:(...e)=>e.reduce(((e,t)=>{let n=0,o=0,r=0;try{n=e.toString().split(".")[1].length}catch(s){}try{o=t.toString().split(".")[1].length}catch(s){}return r=Math.pow(10,Math.max(n,o)),(e*r-t*r)/r})),mutiply:(...e)=>e.reduce(((e,t)=>{let n=0,o=e.toString(),r=t.toString();try{n+=o.split(".")[1].length}catch(s){}try{n+=r.split(".")[1].length}catch(s){}return Number(o.replace(".",""))*Number(r.replace(".",""))/Math.pow(10,n)})),divide:(...e)=>e.reduce(((e,t)=>{let n=0,o=0,r=e.toString(),s=t.toString();try{n=r.split(".")[1].length}catch(i){}try{o=s.split(".")[1].length}catch(i){}return Number(r.replace(".",""))/Number(s.replace(".",""))*Math.pow(10,o-n)}))},l={isWindow:e=>e&&e instanceof Window,getElementPoint(e,t){if(this.isElement(t)||(t=document.body),!this.isContains(t,e))throw new Error("The second argument and the first argument have no hierarchical relationship");const n=e;let o=0,r=0;for(;this.isElement(e)&&this.isContains(t,e)&&t!==e;)o+=e.offsetTop,r+=e.offsetLeft,e=e.offsetParent;return{top:o,left:r,right:t.offsetWidth-r-n.offsetWidth,bottom:t.offsetHeight-o-n.offsetHeight}},isContains:(e,t)=>e===t||(e.contains?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))),isParentNode:(e,t)=>e!==t&&t.parentNode===e,children(e,t){const n=Array.from(e.querySelectorAll(t??"*"));return Array.from(n).filter((t=>this.isParentNode(e,t)))},siblings(e,t){if(!e.parentNode)return[];return Array.from(e.parentNode.querySelectorAll(t??"*")).filter((t=>t.parentNode===e.parentNode&&t!=e))},rem2px(e){const t=this.getCssStyle(document.documentElement,"font-size");return i.mutiply(e,parseFloat(t))},px2rem(e){const t=this.getCssStyle(document.documentElement,"font-size");return i.divide(e,parseFloat(t))},width(e){"string"==typeof e&&e&&(e=document.body.querySelector(e)),this.isElement(e)||(e=document.body);const t=e.clientWidth,n=parseFloat(this.getCssStyle(e,"padding-left")),o=parseFloat(this.getCssStyle(e,"padding-right"));return i.subtract(t,n,o)},height(e){"string"==typeof e&&e&&(e=document.body.querySelector(e)),this.isElement(e)||(e=document.body);const t=e.clientHeight,n=parseFloat(this.getCssStyle(e,"padding-top")),o=parseFloat(this.getCssStyle(e,"padding-bottom"));return i.subtract(t,n,o)},removeClass(e,t){s.trim(t).split(/\s+/).forEach((t=>{e.classList.remove(t)}))},addClass(e,t){s.trim(t).split(/\s+/).forEach((t=>{e.classList.add(t)}))},hasClass:(e,t)=>s.trim(t).split(/\s+/).every((t=>e.classList.contains(t))),scrollTopBottomTrigger(e,t){"string"==typeof e&&e&&(e=document.body.querySelector(e));let n=window;this.isElement(e)&&e!=document.body&&e!=document.documentElement&&(n=e),"function"==typeof e&&(t=e);let o=!0;n.addEventListener("scroll",(()=>{if(this.getScrollTop(n)<=0){if(!o)return;"function"==typeof t&&(o=!1,t({state:"top",target:n}))}else{const e={state:"bottom",target:n};let r=0;if(r=n==window?window.innerHeight:n.clientHeight,i.add(this.getScrollTop(n),r)+1>=this.getScrollHeight(n)&&r!=this.getScrollHeight(n)){if(!o)return;"function"==typeof t&&(o=!1,t(e))}else o=!0}}))},getScrollWidth(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.documentElement&&e!=document.body?e.scrollWidth:0==document.documentElement.scrollWidth||0==document.body.scrollWidth?document.documentElement.scrollWidth||document.body.scrollWidth:document.documentElement.scrollWidth>document.body.scrollWidth?document.documentElement.scrollWidth:document.body.scrollWidth,t},getScrollHeight(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.documentElement&&e!=document.body?e.scrollHeight:0==document.documentElement.scrollHeight||0==document.body.scrollHeight?document.documentElement.scrollHeight||document.body.scrollHeight:document.documentElement.scrollHeight>document.body.scrollHeight?document.documentElement.scrollHeight:document.body.scrollHeight,t},setScrollTop(e){let t=!1,n=e.el;"string"==typeof n&&n&&(n=document.body.querySelector(n));const o=e.number||0,r=e.time||0;return this.isElement(n)&&n!=document.body&&n!=document.documentElement&&n!=window||(t=!0),new Promise((e=>{if(r<=0)t?document.documentElement.scrollTop=document.body.scrollTop=o:n.scrollTop=o,e();else{const s=10;let l=i.divide(r,s),d=this.getScrollTop(n);const c=i.divide(i.subtract(o,d),l),a=setInterval((()=>{l>0?(l--,t?document.documentElement.scrollTop=document.body.scrollTop=d=i.add(d,c):n.scrollTop=d=i.add(d,c)):(clearInterval(a),e())}),s)}}))},getScrollTop(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.body&&e!=document.documentElement&&e!=window?e.scrollTop:0==document.documentElement.scrollTop||0==document.body.scrollTop?document.documentElement.scrollTop||document.body.scrollTop:document.documentElement.scrollTop>document.body.scrollTop?document.documentElement.scrollTop:document.body.scrollTop,t},getScrollLeft(e){"string"==typeof e&&e&&(e=document.body.querySelector(e));let t=0;return t=this.isElement(e)&&e!=document.body&&e!=document.documentElement&&e!=window?e.scrollLeft:0==document.documentElement.scrollLeft||0==document.body.scrollLeft?document.documentElement.scrollLeft||document.body.scrollLeft:document.documentElement.scrollLeft>document.body.scrollLeft?document.documentElement.scrollLeft:document.body.scrollLeft,t},setScrollLeft(e){let t=!1,n=e.el;"string"==typeof n&&n&&(n=document.body.querySelector(n));const o=e.number||0,r=e.time||0;return this.isElement(n)&&n!=document.body&&n!=document.documentElement&&n!=window||(t=!0),new Promise((e=>{if(r<=0)t?document.documentElement.scrollLeft=document.body.scrollLeft=o:n.scrollLeft=o,e();else{const s=10;let l=i.divide(r,s),d=this.getScrollLeft(n);const c=i.divide(i.subtract(o,d),l);let a=setInterval((()=>{l>0?(l--,t?document.documentElement.scrollLeft=document.body.scrollLeft=d=i.add(d,c):n.scrollLeft=d=i.add(d,c)):(clearInterval(a),e())}),s)}}))},getCssStyle(e,t){let n="";return n=document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(e)[t]:e.currentStyle[t],n},getCssSelector(e){if(/^#{1}/.test(e))return{type:"id",value:e.substring(1)};if(/^\./.test(e))return{type:"class",value:e.substring(1)};if(/^\[(.+)\]$/.test(e)){let t="";const n=s.trim(e,!0).substring(1,s.trim(e,!0).length-1).split("=");return 1==n.length&&(t=n[0]),2==n.length&&(t={attributeName:n[0],attributeValue:n[1].replace(/\'/g,"").replace(/\"/g,"")}),{type:"attribute",value:t}}return{type:"tag",value:e}},getElementBounding(e){"string"==typeof e&&e&&(e=document.body.querySelector(e)),this.isElement(e)||(e=document.body);const t=e.getBoundingClientRect();return{top:t.top,bottom:i.subtract(document.documentElement.clientHeight||window.innerHeight,t.bottom),left:t.left,right:i.subtract(document.documentElement.clientWidth||window.innerWidth,t.right)}},isElement:e=>!!e&&e instanceof Node&&1===e.nodeType,string2dom(e){const t=document.createElement("template");return t.innerHTML=e,1==t.content.children.length?t.content.children[0]:Array.from(t.content.children)}},d=e=>{const t=e.split(/[\s]+/g),n=[];return t.forEach((e=>{const t=e.split("."),o={eventName:t[0]};t.length>1&&(o.guid=t[1]),n.push(o)})),n},c=(e,t,n)=>{const o=r.get(e,"dap-defined-events")||{},s=Object.keys(o),i=s.length;for(let r=0;r{const t={};return Object.keys(e).forEach((n=>{e[n]&&(t[n]=e[n])})),t})(o))},a={on(e,t,n,o){d(t).forEach((t=>{((e,t,n,o,s)=>{const i=r.get(e,"dap-defined-events")||{};n||(n=r.get(e,"dap-event-guid")||0,r.set(e,"dap-event-guid",n+1)),i[n=t+"."+n]&&i[n].type==t&&e.removeEventListener(t,i[n].fn,i[n].options),e.addEventListener(t,o,s),i[n]={type:t,fn:o,options:s},r.set(e,"dap-defined-events",i)})(e,t.eventName,t.guid,n.bind(e),o)}))},off(e,t){const n=r.get(e,"dap-defined-events");if(!n)return;if(!t){const t=Object.keys(n),o=t.length;for(let r=0;r{c(e,t.eventName,t.guid)}))},get(e){const t=r.get(e,"dap-defined-events");if(t)return t}},u={getImageUrl:e=>window.URL.createObjectURL(e),dataFileToBase64:e=>new Promise((t=>{const n=new FileReader;n.readAsDataURL(e),n.onloadend=()=>{const e=n.result;t(e)}})),dataBase64toFile(e,t){const n=e.split(","),o=n[0].match(/:(.*?);/)[1],r=atob(n[1]);let s=r.length;const i=new Uint8Array(s);for(;s--;)i[s]=r.charCodeAt(s);return new File([i],t,{type:o})},compressImage(e,t){const n=(e,o,r)=>{let s=e.toDataURL("image/"+(t.mimeType??"jpeg"),r),i=this.dataBase64toFile(s,o);if(t.maxSize&&t.maxSize>0&&i.size>1024*t.maxSize){r=r<=0?0:Number((r-.01).toFixed(2));const t=n(e,o,r);s=t.url,i=t.file,r=t.quality}return{file:i,url:s,quality:r}};return new Promise(((o,r)=>{const s=new FileReader;s.readAsDataURL(e),s.onload=()=>{const i=s.result,l=new Image;l.src=i,l.onload=()=>{if(t.minSize&&t.minSize>0&&e.size<=1024*t.minSize)return void o({file:e,url:i,quality:1,width:l.width,height:l.height});const r=document.createElement("canvas"),s=r.getContext("2d");r.width=t.width||l.width,r.height=t.width?t.width/(l.width/l.height):l.height,s.drawImage(l,0,0,r.width,r.height);const d=e.name.lastIndexOf("."),c=e.name.substring(0,d)+"."+(t.mimeType??"jpeg");let a=n(r,c,t.quality??.8);o({...a,width:r.width,height:r.height})},l.onerror=()=>{r(new Error("Failed to load image file"))}},s.onerror=()=>{r(new Error("Failed to load image file"))}}))}},m={language:()=>window.navigator.language,device(){const e=window.navigator.userAgent;return{PC:!e.match(/AppleWebKit.*Mobile.*/),Mobile:!!e.match(/AppleWebKit.*Mobile.*/),iPhone:e.includes("iPhone"),Phone:e.includes("Android")&&/(?:Mobile)/.test(e)||e.includes("iPhone")||/(?:Windows Phone)/.test(e),iPad:e.includes("iPad"),Tablet:e.includes("iPad")||e.includes("Android")&&!/(?:Mobile)/.test(e)||e.includes("Firefox")&&/(?:Tablet)/.test(e),WindowsPhone:/(?:Windows Phone)/.test(e)}},browser(){const e=window.navigator.userAgent;return{Edge:!!e.match(/Edg\/([\d.]+)/),Weixin:e.includes("MicroMessenger"),QQ:e.includes("QQ"),QQBrowser:e.includes("MQQBrowser"),UC:e.includes("UCBrowser"),Chrome:e.includes("Chrome"),Firefox:e.includes("Firefox"),Sougou:e.toLocaleLowerCase().includes("se 2.x")||e.toLocaleLowerCase().includes("metasr")||e.toLocaleLowerCase().includes("sogou"),Safari:e.includes("Safari")&&!e.includes("Chrome")}},browserKernel(){const e=window.navigator.userAgent;return e.includes("Presto")?"opera":e.includes("AppleWebKit")?"webkit":e.includes("Gecko")&&!e.includes("KHTML")?"gecko":void 0},os(){const e=window.navigator.userAgent;return{Windows:e.includes("Windows"),WindowsCPU:e.toLocaleLowerCase().includes("win64")||e.toLocaleLowerCase().includes("wow64")?"x64":e.toLocaleLowerCase().includes("win32")||e.toLocaleLowerCase().includes("wow32")?"x32":void 0,WindowsVersion:e.includes("Windows NT 6.1")||e.includes("Windows 7")?"win7":e.includes("Windows NT 6.3")||e.includes("Windows NT 6.2")||e.includes("Windows NT 8")?"win8":e.includes("Windows NT 10")||e.includes("Windows NT 6.4")?"win10":void 0,Mac:e.includes("Macintosh"),MacVersion:function(){if(e.includes("Macintosh")){const t=e.match(/Mac OS X ([^\s]+)\)/);if(t&&t[1])return t[1].split(/_|\./).join(".")}return""}(),ios:!!e.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),iosVersion:function(){if(e.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)){const t=e.match(/CPU.+OS ([^\s]+) like Mac OS X/);if(t&&t[1])return t[1].split(/_|\./).join(".")}return""}(),Android:e.includes("Android"),AndroidVersion:function(){const t=e.match(/Android ([^\s]+);/);return t&&t[1]?t[1].split(/_|\./).join("."):""}(),Linux:e.includes("Linux"),HarmonyOS:e.includes("HarmonyOS"),Ubuntu:e.includes("Ubuntu")}}},h={start(e,t){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");const n=new SpeechSynthesisUtterance;n.text=e,n.pitch=(null==t?void 0:t.pitch)??.8,n.rate=(null==t?void 0:t.rate)??1,n.volume=(null==t?void 0:t.volume)??1,n.lang="zh-CN",n.onstart=o=>{var r;null==(r=null==t?void 0:t.start)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onend=o=>{var r;null==(r=null==t?void 0:t.end)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onpause=o=>{var r;null==(r=null==t?void 0:t.pause)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onresume=o=>{var r;null==(r=null==t?void 0:t.resume)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},n.onerror=o=>{var r;null==(r=null==t?void 0:t.error)||r.apply(n,[o,{text:e,pitch:t.pitch??.8,rate:t.rate??1,volume:t.volume??1}])},window.speechSynthesis.speak(n)},stop(){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");window.speechSynthesis.cancel()},pause(){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");window.speechSynthesis.pause()},resume(){if(!window.SpeechSynthesisUtterance||!window.speechSynthesis)throw new Error("The current browser does not support this syntax");window.speechSynthesis.resume()}},p={number:i,data:r,element:l,event:a,common:t,color:n,file:u,string:s,platform:m,speech:h};e.color=n,e.common=t,e.data=r,e.default=p,e.element=l,e.event=a,e.file=u,e.number=i,e.platform=m,e.speech=h,e.string=s,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
2 |
--------------------------------------------------------------------------------
/src/element.ts:
--------------------------------------------------------------------------------
1 | import { string as stringUtil } from './string'
2 | import { number as numberUtil } from './number'
3 |
4 | export type ScrollOptionsType = {
5 | el?: HTMLElement | string | Window
6 | time?: number
7 | number?: number
8 | }
9 |
10 | export type PlacementType = {
11 | left: number
12 | top: number
13 | right: number
14 | bottom: number
15 | }
16 |
17 | export type scrollTopBottomResultType = {
18 | state: 'top' | 'bottom'
19 | target: HTMLElement | Window
20 | }
21 |
22 | /**
23 | * element相关工具方法
24 | */
25 | export const element = {
26 | /**
27 | * 判断是否是Window对象
28 | * @param {Object} data 入参
29 | */
30 | isWindow(data: any) {
31 | return (data && data instanceof Window) as boolean
32 | },
33 | /**
34 | * 获取元素距离某个定位祖先元素左侧/顶部/底部/右侧的距离
35 | * @param {Object} el 元素
36 | * @param {Object} root 定位父元素或者祖先元素,未指定则为document.body
37 | */
38 | getElementPoint(el: HTMLElement, root?: HTMLElement): PlacementType {
39 | if (!this.isElement(root)) {
40 | root = document.body
41 | }
42 | if (!this.isContains(root!, el)) {
43 | throw new Error('The second argument and the first argument have no hierarchical relationship')
44 | }
45 |
46 | const obj = el
47 | let offsetTop = 0
48 | let offsetLeft = 0
49 | //判断是否有定位父容器,如果存在则累加其边距
50 | while (this.isElement(el) && this.isContains(root, el) && root !== el) {
51 | offsetTop += el.offsetTop //叠加父容器的上边距
52 | offsetLeft += el.offsetLeft //叠加父容器的左边距
53 | el = el.offsetParent as HTMLElement
54 | }
55 | let offsetRight = root!.offsetWidth - offsetLeft - obj.offsetWidth
56 | let offsetBottom = root!.offsetHeight - offsetTop - obj.offsetHeight
57 | return {
58 | top: offsetTop,
59 | left: offsetLeft,
60 | right: offsetRight,
61 | bottom: offsetBottom
62 | }
63 | },
64 |
65 | /**
66 | * 判断某个元素是否包含指定元素,包含相等关系和父子关系
67 | * @param {Object} parentNode 父元素或祖先元素
68 | * @param {Object} childNode 子元素
69 | */
70 | isContains(parentNode: HTMLElement, childNode: HTMLElement) {
71 | if (parentNode === childNode) {
72 | return true
73 | }
74 | //如果浏览器支持contains
75 | if (parentNode.contains) {
76 | return parentNode.contains(childNode)
77 | }
78 | //火狐支持
79 | if (parentNode.compareDocumentPosition) {
80 | return !!(parentNode.compareDocumentPosition(childNode) & 16)
81 | }
82 | return false
83 | },
84 |
85 | /**
86 | * 判断某个元素是否是指定元素的父元素
87 | * @param {Object} parentNode 父元素
88 | * @param {Object} childNode 子元素
89 | */
90 | isParentNode(parentNode: HTMLElement, childNode: HTMLElement) {
91 | if (parentNode === childNode) {
92 | return false
93 | }
94 | return childNode.parentNode === parentNode
95 | },
96 |
97 | /**
98 | * 查找某个元素下指定选择器的子元素
99 | * @param {Object} el 元素
100 | * @param {Object} selector 支持多选择器,等同于querySelectorAll的参数
101 | */
102 | children(el: HTMLElement, selector?: string) {
103 | const elements: HTMLElement[] = Array.from(el.querySelectorAll(selector ?? '*'))
104 | return Array.from(elements).filter(elm => this.isParentNode(el, elm))
105 | },
106 |
107 | /**
108 | * 查找某个元素下指定选择器的兄弟元素
109 | * @param {Object} el 元素
110 | * @param {Object} selector 取值等同于queryselectorAll的参数,支持多选择器
111 | */
112 | siblings(el: HTMLElement, selector?: string) {
113 | if (!el.parentNode) {
114 | return [] as HTMLElement[]
115 | }
116 | const elements: HTMLElement[] = Array.from(el.parentNode.querySelectorAll(selector ?? '*'))
117 | return elements.filter(elm => elm.parentNode === el.parentNode && elm != el)
118 | },
119 |
120 | /**
121 | * rem与px单位转换
122 | * @param {Object} num rem数值
123 | */
124 | rem2px(num: number) {
125 | const fs = this.getCssStyle(document.documentElement, 'font-size')
126 | return numberUtil.mutiply(num, parseFloat(fs))
127 | },
128 |
129 | /**
130 | * rem与px单位转换
131 | * @param {Object} num px数值
132 | */
133 | px2rem(num: number) {
134 | const fs = this.getCssStyle(document.documentElement, 'font-size')
135 | return numberUtil.divide(num, parseFloat(fs))
136 | },
137 |
138 | /**
139 | * 获取元素的内容宽度,内容宽度不包括border和padding
140 | * @param {Object} el 支持css选择器字符串,未指定则表示document.body
141 | */
142 | width(el?: HTMLElement | string) {
143 | if (typeof el == 'string' && el) {
144 | el = document.body.querySelector(el) as HTMLElement
145 | }
146 | if (!this.isElement(el)) {
147 | el = document.body
148 | }
149 | const clientWidth = (el as HTMLElement).clientWidth //获取元素包括内边距在内的宽度
150 | const paddingLeft_width = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-left')) //左内边距
151 | const paddingRight_width = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-right')) //右内边距宽度
152 | return numberUtil.subtract(clientWidth, paddingLeft_width, paddingRight_width)
153 | },
154 |
155 | /**
156 | * 获取元素的内容高度,内容高度不包括border和padding
157 | * @param {Object} el 支持css选择器字符串 未指定则表示document.body
158 | */
159 | height(el?: HTMLElement | string) {
160 | if (typeof el == 'string' && el) {
161 | el = document.body.querySelector(el) as HTMLElement
162 | }
163 | if (!this.isElement(el)) {
164 | el = document.body
165 | }
166 | const clientHeight = (el as HTMLElement).clientHeight //获取元素包括内边距在内的高度
167 | const paddingTop_height = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-top')) //上内边距
168 | const paddingBottom_height = parseFloat(this.getCssStyle(el as HTMLElement, 'padding-bottom')) //下内边距宽度
169 | return numberUtil.subtract(clientHeight, paddingTop_height, paddingBottom_height)
170 | },
171 |
172 | /**
173 | * 移除class
174 | * @param {Object} el 元素
175 | * @param {Object} className 支持多类,以空格划分
176 | */
177 | removeClass(el: HTMLElement, className: string) {
178 | const classArray = stringUtil.trim(className).split(/\s+/) //按照空格划分
179 | classArray.forEach(item => {
180 | el.classList.remove(item)
181 | })
182 | },
183 |
184 | /**
185 | * 添加class
186 | * @param {Object} el 元素
187 | * @param {Object} className 支持多类,以空格划分
188 | */
189 | addClass(el: HTMLElement, className: string) {
190 | const classArray = stringUtil.trim(className).split(/\s+/) //按照空格划分
191 | classArray.forEach(item => {
192 | el.classList.add(item)
193 | })
194 | },
195 |
196 | /**
197 | * 判断指定元素是否含有指定类名
198 | * @param {Object} el 元素
199 | * @param {Object} className 支持多类,以空格划分
200 | */
201 | hasClass(el: HTMLElement, className: string) {
202 | const classArray = stringUtil.trim(className).split(/\s+/) //按照空格划分
203 | return classArray.every(item => {
204 | return el.classList.contains(item)
205 | })
206 | },
207 |
208 | /**
209 | * 监听元素滚动到顶部或者底部
210 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
211 | * @param {Object} callback 回调函数
212 | */
213 | scrollTopBottomTrigger(el?: HTMLElement | string | Window, callback?: (options: scrollTopBottomResultType) => void) {
214 | if (typeof el == 'string' && el) {
215 | el = document.body.querySelector(el) as HTMLElement
216 | }
217 | let scrollEle: Window | HTMLElement = window
218 | if (this.isElement(el) && el != document.body && el != document.documentElement) {
219 | scrollEle = el as HTMLElement
220 | }
221 | if (typeof el == 'function') {
222 | callback = el
223 | }
224 | //滑动到底部时是否触发回调函数的标识,解决ios系统下多次触发回调的bug
225 | let flag = true
226 | scrollEle.addEventListener('scroll', () => {
227 | if (this.getScrollTop(scrollEle) <= 0) {
228 | //滑动到顶部
229 | const options: scrollTopBottomResultType = {
230 | state: 'top',
231 | target: scrollEle
232 | }
233 | if (!flag) {
234 | return
235 | }
236 | if (typeof callback == 'function') {
237 | flag = false
238 | callback(options)
239 | }
240 | } else {
241 | //滑动到底部
242 | const options: scrollTopBottomResultType = {
243 | state: 'bottom',
244 | target: scrollEle
245 | }
246 | let height = 0
247 | if (scrollEle == window) {
248 | height = window.innerHeight
249 | } else {
250 | height = (scrollEle as HTMLElement).clientHeight
251 | }
252 | //+1是为了防止出现小数点误差
253 | if (numberUtil.add(this.getScrollTop(scrollEle), height) + 1 >= this.getScrollHeight(scrollEle as HTMLElement) && height != this.getScrollHeight(scrollEle as HTMLElement)) {
254 | if (!flag) {
255 | return
256 | }
257 | if (typeof callback == 'function') {
258 | flag = false
259 | callback(options)
260 | }
261 | } else {
262 | flag = true
263 | }
264 | }
265 | })
266 | },
267 |
268 | /**
269 | * 获取文档或元素的总宽度
270 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档
271 | */
272 | getScrollWidth(el?: HTMLElement | string) {
273 | if (typeof el == 'string' && el) {
274 | el = document.body.querySelector(el) as HTMLElement
275 | }
276 | let scrollWidth = 0
277 | if (this.isElement(el) && el != document.documentElement && el != document.body) {
278 | scrollWidth = (el as HTMLElement).scrollWidth
279 | } else {
280 | if (document.documentElement.scrollWidth == 0 || document.body.scrollWidth == 0) {
281 | scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth
282 | } else {
283 | scrollWidth = document.documentElement.scrollWidth > document.body.scrollWidth ? document.documentElement.scrollWidth : document.body.scrollWidth
284 | }
285 | }
286 | return scrollWidth
287 | },
288 |
289 | /**
290 | * 获取文档或者元素的总高度
291 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档
292 | */
293 | getScrollHeight(el?: HTMLElement | string) {
294 | if (typeof el == 'string' && el) {
295 | el = document.body.querySelector(el) as HTMLElement
296 | }
297 | let scrollHeight = 0
298 | if (this.isElement(el) && el != document.documentElement && el != document.body) {
299 | scrollHeight = (el as HTMLElement).scrollHeight
300 | } else {
301 | if (document.documentElement.scrollHeight == 0 || document.body.scrollHeight == 0) {
302 | scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight
303 | } else {
304 | scrollHeight = document.documentElement.scrollHeight > document.body.scrollHeight ? document.documentElement.scrollHeight : document.body.scrollHeight
305 | }
306 | }
307 | return scrollHeight
308 | },
309 |
310 | /**
311 | * 设置滚动条在Y轴上的距离
312 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动
313 | */
314 | setScrollTop(options: ScrollOptionsType) {
315 | let isWindow = false
316 | let el = options.el
317 | if (typeof el == 'string' && el) {
318 | el = document.body.querySelector(el) as HTMLElement
319 | }
320 | const number = options.number || 0
321 | const time = options.time || 0
322 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) {
323 | isWindow = true
324 | }
325 | return new Promise(resolve => {
326 | if (time <= 0) {
327 | if (isWindow) {
328 | document.documentElement.scrollTop = document.body.scrollTop = number
329 | } else {
330 | ;(el as HTMLElement).scrollTop = number
331 | }
332 | resolve()
333 | } else {
334 | const spacingTime = 10 // 设置循环的间隔时间 值越小消耗性能越高
335 | let spacingIndex = numberUtil.divide(time, spacingTime) // 计算循环的次数
336 | // 获取当前滚动条位置
337 | let nowTop = this.getScrollTop(el!)
338 | const everTop = numberUtil.divide(numberUtil.subtract(number, nowTop), spacingIndex) // 计算每次滑动的距离
339 | const scrollTimer = setInterval(() => {
340 | if (spacingIndex > 0) {
341 | spacingIndex--
342 | if (isWindow) {
343 | document.documentElement.scrollTop = document.body.scrollTop = nowTop = numberUtil.add(nowTop, everTop)
344 | } else {
345 | ;(el as HTMLElement).scrollTop = nowTop = numberUtil.add(nowTop, everTop)
346 | }
347 | } else {
348 | clearInterval(scrollTimer) // 清除计时器
349 | resolve()
350 | }
351 | }, spacingTime)
352 | }
353 | })
354 | },
355 |
356 | /**
357 | * 获取滚动条在Y轴上滚动的距离
358 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
359 | */
360 | getScrollTop(el?: HTMLElement | string | Window) {
361 | if (typeof el == 'string' && el) {
362 | el = document.body.querySelector(el) as HTMLElement
363 | }
364 | let scrollTop = 0
365 | //如果是元素
366 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) {
367 | scrollTop = (el as HTMLElement).scrollTop
368 | } else {
369 | if (document.documentElement.scrollTop == 0 || document.body.scrollTop == 0) {
370 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop
371 | } else {
372 | scrollTop = document.documentElement.scrollTop > document.body.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop
373 | }
374 | }
375 | return scrollTop
376 | },
377 |
378 | /**
379 | * 获取滚动条在X轴上滚动的距离
380 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
381 | */
382 | getScrollLeft(el?: HTMLElement | string | Window) {
383 | if (typeof el == 'string' && el) {
384 | el = document.body.querySelector(el) as HTMLElement
385 | }
386 | let scrollLeft = 0
387 | //如果是元素
388 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) {
389 | scrollLeft = (el as HTMLElement).scrollLeft
390 | } else {
391 | if (document.documentElement.scrollLeft == 0 || document.body.scrollLeft == 0) {
392 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
393 | } else {
394 | scrollLeft = document.documentElement.scrollLeft > document.body.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft
395 | }
396 | }
397 | return scrollLeft
398 | },
399 |
400 | /**
401 | * 设置滚动条在X轴上的距离
402 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动
403 | */
404 | setScrollLeft(options: ScrollOptionsType) {
405 | let isWindow = false
406 | let el = options.el
407 | if (typeof el == 'string' && el) {
408 | el = document.body.querySelector(el) as HTMLElement
409 | }
410 | const number = options.number || 0
411 | const time = options.time || 0
412 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) {
413 | isWindow = true
414 | }
415 | return new Promise(resolve => {
416 | if (time <= 0) {
417 | if (isWindow) {
418 | document.documentElement.scrollLeft = document.body.scrollLeft = number
419 | } else {
420 | ;(el as HTMLElement).scrollLeft = number
421 | }
422 | resolve()
423 | } else {
424 | const spacingTime = 10 // 设置循环的间隔时间 值越小消耗性能越高
425 | let spacingIndex = numberUtil.divide(time, spacingTime) // 计算循环的次数
426 | // 获取当前滚动条位置
427 | let nowLeft = this.getScrollLeft(el)
428 | const everLeft = numberUtil.divide(numberUtil.subtract(number, nowLeft), spacingIndex) // 计算每次滑动的距离
429 | let scrollTimer = setInterval(() => {
430 | if (spacingIndex > 0) {
431 | spacingIndex--
432 | if (isWindow) {
433 | document.documentElement.scrollLeft = document.body.scrollLeft = nowLeft = numberUtil.add(nowLeft, everLeft)
434 | } else {
435 | ;(el as HTMLElement).scrollLeft = nowLeft = numberUtil.add(nowLeft, everLeft)
436 | }
437 | } else {
438 | clearInterval(scrollTimer) // 清除计时器
439 | resolve()
440 | }
441 | }, spacingTime)
442 | }
443 | })
444 | },
445 |
446 | /**
447 | * 获取元素指定样式
448 | * @param {Object} el 元素
449 | * @param {Object} cssName 样式名称
450 | */
451 | getCssStyle(el: HTMLElement, cssName: string) {
452 | let cssText = ''
453 | //兼容IE9-IE11、chrome、firefox、safari、opera;不兼容IE7-IE8
454 | if (document.defaultView && document.defaultView.getComputedStyle) {
455 | cssText = (document.defaultView.getComputedStyle(el) as any)[cssName]
456 | }
457 | //兼容IE7-IE11;不兼容chrome、firefox、safari、opera
458 | else {
459 | cssText = (el as any).currentStyle[cssName]
460 | }
461 | return cssText
462 | },
463 |
464 | /**
465 | * 判断字符串属于哪种选择器
466 | * @param {Object} selector
467 | */
468 | getCssSelector(selector: string): { type: 'id' | 'class' | 'attribute' | 'tag'; value: string | { attributeName: string; attributeValue: string } } {
469 | //id选择器,以#开头的字符串
470 | if (/^#{1}/.test(selector)) {
471 | return {
472 | type: 'id',
473 | value: selector.substring(1)
474 | }
475 | }
476 | //类名选择器,以.开头的字符串
477 | if (/^\./.test(selector)) {
478 | return {
479 | type: 'class',
480 | value: selector.substring(1)
481 | }
482 | }
483 | //属性选择器,以[]包裹的字符串
484 | if (/^\[(.+)\]$/.test(selector)) {
485 | let value: string | { attributeName: string; attributeValue: string } = ''
486 | const attribute = stringUtil.trim(selector, true).substring(1, stringUtil.trim(selector, true).length - 1)
487 | const arry = attribute.split('=')
488 | if (arry.length == 1) {
489 | value = arry[0]
490 | }
491 | if (arry.length == 2) {
492 | value = {
493 | attributeName: arry[0],
494 | attributeValue: arry[1].replace(/\'/g, '').replace(/\"/g, '') //去除属性值的单引号或者双引号
495 | }
496 | }
497 | return {
498 | type: 'attribute',
499 | value
500 | }
501 | }
502 |
503 | //默认为标签名选择器
504 | return {
505 | type: 'tag',
506 | value: selector
507 | }
508 | },
509 |
510 | /**
511 | * 获取元素距离可视窗口的位置
512 | * @param {Object} el 支持css选择器字符串 未指定则为document.body
513 | */
514 | getElementBounding(el?: HTMLElement | string): PlacementType {
515 | if (typeof el == 'string' && el) {
516 | el = document.body.querySelector(el) as HTMLElement
517 | }
518 | if (!this.isElement(el)) {
519 | el = document.body
520 | }
521 | const point = (el as HTMLElement).getBoundingClientRect()
522 | const top = point.top //元素顶部距离可视窗口上边的距离
523 | const bottom = numberUtil.subtract(document.documentElement.clientHeight || window.innerHeight, point.bottom) //元素底部距离可视窗口底部的距离
524 | const left = point.left //元素左侧距离可视窗口左边的距离
525 | const right = numberUtil.subtract(document.documentElement.clientWidth || window.innerWidth, point.right) //元素右侧距离可视窗口右边的距离
526 | return {
527 | top,
528 | bottom,
529 | left,
530 | right
531 | }
532 | },
533 |
534 | /**
535 | * 判断是否是元素
536 | * @param {Object} el
537 | */
538 | isElement(el: any) {
539 | return !!el && el instanceof Node && el.nodeType === 1
540 | },
541 |
542 | /**
543 | * 字符串转dom
544 | * @param {Object} html
545 | */
546 | string2dom(html: string) {
547 | const template = document.createElement('template')
548 | template.innerHTML = html
549 | if (template.content.children.length == 1) {
550 | return template.content.children[0] as HTMLElement
551 | }
552 | return Array.from(template.content.children) as HTMLElement[]
553 | }
554 | }
555 |
--------------------------------------------------------------------------------
/lib/dap-util.es.js:
--------------------------------------------------------------------------------
1 | const common = {
2 | /**
3 | * 常用判断
4 | * @param {Object} text 要判断的字符串
5 | * @param {Object} param 判断的类型字符串
6 | */
7 | matchingText(text, param) {
8 | if (param == "Chinese") {
9 | return /^[\u4e00-\u9fa5]+$/.test(text);
10 | }
11 | if (param == "chinese") {
12 | return /[\u4e00-\u9fa5]/.test(text);
13 | }
14 | if (param == "email") {
15 | return /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(text);
16 | }
17 | if (param == "username") {
18 | return /^[a-zA-Z0-9_]{4,16}$/.test(text);
19 | }
20 | if (param == "int+") {
21 | return /^\d+$/.test(text);
22 | }
23 | if (param == "int-") {
24 | return /^-\d+$/.test(text);
25 | }
26 | if (param == "int") {
27 | return /^-?\d+$/.test(text);
28 | }
29 | if (param == "pos") {
30 | return /^\d*\.?\d+$/.test(text);
31 | }
32 | if (param == "neg") {
33 | return /^-\d*\.?\d+$/.test(text);
34 | }
35 | if (param == "number") {
36 | return /^-?\d*\.?\d+$/.test(text);
37 | }
38 | if (param == "phone") {
39 | return /^1[0-9]\d{9}$/.test(text);
40 | }
41 | if (param == "idCard") {
42 | return /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(text);
43 | }
44 | if (param == "url") {
45 | return /^(https?|ftp):\/\/(-\.)?([^\s\/?\.#-]+\.?)+(\/[^\s]*)?$/.test(text);
46 | }
47 | if (param == "IPv4") {
48 | return /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(text);
49 | }
50 | if (param == "hex") {
51 | return /^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/.test(text);
52 | }
53 | if (param == "rgb") {
54 | return /^rgb\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\)$/.test(text);
55 | }
56 | if (param == "rgba") {
57 | return /^rgba\((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d),\s?(0?\.\d|1(\.0)?|0)\)$/.test(text);
58 | }
59 | if (param == "QQ") {
60 | return /^[1-9][0-9]{4,10}$/.test(text);
61 | }
62 | if (param == "weixin") {
63 | return /^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$/.test(text);
64 | }
65 | if (param == "plate") {
66 | return /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$|^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{1}[D|F]{1}[A-Z0-9]{5,6}$/.test(text);
67 | }
68 | return false;
69 | },
70 | /**
71 | * 根据参数名获取地址栏参数值
72 | * @param {Object} name
73 | */
74 | getUrlParams(name) {
75 | const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
76 | let search = window.location.search.substring(1);
77 | if (!search) {
78 | const arr = window.location.hash.split("?");
79 | if (arr.length == 2) {
80 | search = arr[1];
81 | }
82 | }
83 | let r = search.match(reg);
84 | if (r) {
85 | return decodeURIComponent(r[2]);
86 | }
87 | },
88 | /**
89 | * 判断是否空对象
90 | * @param {Object} obj
91 | */
92 | isEmptyObject(obj) {
93 | return this.isObject(obj) && Object.keys(obj).length == 0;
94 | },
95 | /**
96 | * 判断两个参数是否相等
97 | * @param {Object} a
98 | * @param {Object} b
99 | */
100 | equal(a, b) {
101 | if (typeof a !== typeof b) {
102 | return false;
103 | }
104 | if (this.isObject(a) && this.isObject(b)) {
105 | const aProps = Object.getOwnPropertyNames(a);
106 | const bProps = Object.getOwnPropertyNames(b);
107 | if (aProps.length != bProps.length) {
108 | return false;
109 | }
110 | const length = aProps.length;
111 | let isEqual = true;
112 | for (let i = 0; i < length; i++) {
113 | const propName = aProps[i];
114 | const propA = a[propName];
115 | const propB = b[propName];
116 | if (!this.equal(propA, propB)) {
117 | isEqual = false;
118 | break;
119 | }
120 | }
121 | return isEqual;
122 | }
123 | return a === b;
124 | },
125 | /**
126 | * 是否对象
127 | * @param {Object} val
128 | */
129 | isObject(val) {
130 | return typeof val === "object" && !!val;
131 | },
132 | /**
133 | * 文本复制
134 | * @param {Object} text
135 | */
136 | copyText(text) {
137 | if (!navigator.clipboard) {
138 | throw new Error("navigator.clipboard must be obtained in a secure environment, such as localhost, 127.0.0.1, or https, so the method won't work");
139 | }
140 | return navigator.clipboard.writeText(text);
141 | },
142 | /**
143 | * 深度克隆
144 | * @param {Object} data
145 | */
146 | clone(data2) {
147 | if (this.isObject(data2)) {
148 | if (Array.isArray(data2)) {
149 | return data2.map((item) => {
150 | return this.clone(item);
151 | });
152 | }
153 | const newData = {};
154 | for (let key in data2) {
155 | newData[key] = this.clone(data2[key]);
156 | }
157 | return newData;
158 | }
159 | return data2;
160 | }
161 | };
162 | const color = {
163 | /**
164 | * rgb转hsv值
165 | * @param {Object} rgb rgb值,数组
166 | */
167 | rgb2hsv(rgb) {
168 | if (rgb.length != 3) {
169 | throw new TypeError("Invalid argument");
170 | }
171 | let h = 0;
172 | let s = 0;
173 | let v = 0;
174 | let r = rgb[0] >= 255 ? 255 : rgb[0];
175 | let g = rgb[1] >= 255 ? 255 : rgb[1];
176 | let b = rgb[2] >= 255 ? 255 : rgb[2];
177 | r = r <= 0 ? 0 : r;
178 | g = g <= 0 ? 0 : g;
179 | b = b <= 0 ? 0 : b;
180 | let max = Math.max(r, g, b);
181 | let min = Math.min(r, g, b);
182 | v = max / 255;
183 | if (max === 0) {
184 | s = 0;
185 | } else {
186 | s = 1 - min / max;
187 | }
188 | if (max === min) {
189 | h = 0;
190 | } else if (max === r && g >= b) {
191 | h = 60 * ((g - b) / (max - min)) + 0;
192 | } else if (max === r && g < b) {
193 | h = 60 * ((g - b) / (max - min)) + 360;
194 | } else if (max === g) {
195 | h = 60 * ((b - r) / (max - min)) + 120;
196 | } else if (max === b) {
197 | h = 60 * ((r - g) / (max - min)) + 240;
198 | }
199 | return [h, s * 100, v * 100];
200 | },
201 | /**
202 | * hsv格式值转rgb值
203 | * @param {Object} hsv hsv值,数组
204 | */
205 | hsv2rgb(hsv) {
206 | if (hsv.length != 3) {
207 | throw new TypeError("Invalid argument");
208 | }
209 | let h = hsv[0] >= 360 || hsv[0] <= 0 ? 0 : hsv[0];
210 | let s = hsv[1] >= 100 ? 100 : hsv[1];
211 | s = s <= 0 ? 0 : s;
212 | let v = hsv[2] >= 100 ? 100 : hsv[2];
213 | v = v <= 0 ? 0 : v;
214 | s = s / 100;
215 | v = v / 100;
216 | let r = 0;
217 | let g = 0;
218 | let b = 0;
219 | let i = parseInt(h / 60 % 6 + "");
220 | let f = h / 60 - i;
221 | let p = v * (1 - s);
222 | let q = v * (1 - f * s);
223 | let t = v * (1 - (1 - f) * s);
224 | switch (i) {
225 | case 0:
226 | r = v;
227 | g = t;
228 | b = p;
229 | break;
230 | case 1:
231 | r = q;
232 | g = v;
233 | b = p;
234 | break;
235 | case 2:
236 | r = p;
237 | g = v;
238 | b = t;
239 | break;
240 | case 3:
241 | r = p;
242 | g = q;
243 | b = v;
244 | break;
245 | case 4:
246 | r = t;
247 | g = p;
248 | b = v;
249 | break;
250 | case 5:
251 | r = v;
252 | g = p;
253 | b = q;
254 | break;
255 | }
256 | r = parseInt(r * 255 + "");
257 | g = parseInt(g * 255 + "");
258 | b = parseInt(b * 255 + "");
259 | return [r, g, b];
260 | },
261 | /**
262 | * rgb值转十六进制
263 | * @param {Array} rgb rgb值,数组
264 | */
265 | rgb2hex(rgb) {
266 | if (rgb.length != 3) {
267 | throw new TypeError("Invalid argument");
268 | }
269 | let r = rgb[0];
270 | let g = rgb[1];
271 | let b = rgb[2];
272 | let hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
273 | return hex;
274 | },
275 | /**
276 | * 十六进制颜色转rgb
277 | * @param {String} hex 十六进制颜色值
278 | */
279 | hex2rgb(hex) {
280 | let color2 = hex.toLowerCase();
281 | if (!common.matchingText(color2, "hex")) {
282 | throw new TypeError("The argument must be a hexadecimal color value");
283 | }
284 | if (color2.length === 4) {
285 | let colorNew = "#";
286 | for (let i = 1; i < 4; i += 1) {
287 | colorNew += color2.slice(i, i + 1).concat(color2.slice(i, i + 1));
288 | }
289 | color2 = colorNew;
290 | }
291 | let colorChange = [];
292 | for (let i = 1; i < 7; i += 2) {
293 | colorChange.push(parseInt("0x" + color2.slice(i, i + 2)));
294 | }
295 | return colorChange;
296 | }
297 | };
298 | const dataName = "_dap-datas";
299 | const data = {
300 | /**
301 | * 移除指定数据
302 | * @param {Object} el
303 | * @param {Object} key
304 | */
305 | remove(el, key) {
306 | const data2 = el[dataName] || {};
307 | if (key) {
308 | delete data2[key];
309 | el[dataName] = data2;
310 | } else {
311 | el[dataName] = {};
312 | }
313 | },
314 | /**
315 | * 判断是否含有指定数据
316 | * @param {Object} el
317 | * @param {Object} key
318 | */
319 | has(el, key) {
320 | return (el[dataName] || {}).hasOwnProperty(key);
321 | },
322 | /**
323 | * 获取元素指定数据
324 | * @param {Object} el
325 | * @param {Object} key
326 | */
327 | get(el, key) {
328 | const data2 = el[dataName] || {};
329 | return !!key ? data2[key] : data2;
330 | },
331 | /**
332 | * 设置元素指定数据
333 | * @param {Object} el
334 | * @param {Object} key
335 | * @param {Object} value
336 | */
337 | set(el, key, value) {
338 | const data2 = el[dataName] || {};
339 | data2[key] = value;
340 | el[dataName] = data2;
341 | }
342 | };
343 | const string = {
344 | /**
345 | * 向指定位置插入字符串
346 | * @param {Object} original 原始字符串
347 | * @param {Object} str 插入的字符串
348 | * @param {Object} index 插入的位置
349 | */
350 | insert(original, str, index2) {
351 | if (index2 < 0) {
352 | throw new Error("The third argument cannot be less than 0");
353 | }
354 | return original.substring(0, index2) + str + original.substring(index2, original.length);
355 | },
356 | /**
357 | * 删除指定位置的字符串
358 | * @param {Object} original 原始字符串
359 | * @param {Object} index 删除的位置序列
360 | * @param {Object} num 删除的字符串长度
361 | */
362 | delete(original, index2, num) {
363 | if (index2 < 0) {
364 | throw new Error("The second argument cannot be less than 0");
365 | }
366 | if (num < 0) {
367 | throw new Error("The third argument cannot be less than 0");
368 | }
369 | return original.substring(0, index2) + original.substring(index2 + num, original.length);
370 | },
371 | /**
372 | * 替换指定位置的字符串
373 | * @param {Object} original 原始字符串
374 | * @param {Object} start 开始位置
375 | * @param {Object} end 结束位置
376 | * @param {Object} str 替换的字符串
377 | */
378 | replace(original, start, end, str) {
379 | if (start < 0) {
380 | throw new Error("The second argument cannot be less than 0");
381 | }
382 | if (end < 0) {
383 | throw new Error("The third argument cannot be less than 0");
384 | }
385 | return original.substring(0, start) + str + original.substring(end, original.length);
386 | },
387 | /**
388 | * 去除字符串空格
389 | * @param {Object} str 原始字符串
390 | * @param {Object} global 为true时去除所有空格,否则只去除两边空格
391 | */
392 | trim(str, global) {
393 | let result = str.replace(/(^\s+)|(\s+$)/g, "");
394 | if (global) {
395 | result = result.replace(/\s/g, "");
396 | }
397 | return result;
398 | }
399 | };
400 | const number = {
401 | /**
402 | * 数字格式化
403 | * @param {Number} num
404 | */
405 | formatNumber(num) {
406 | if (this.isNumber(num)) {
407 | return num.toString().replace(/(\d)(?=(?:\d{3})+$)/g, "$1,");
408 | }
409 | return num.toString();
410 | },
411 | /**
412 | * 判断是否数字
413 | * @param {Object} num
414 | */
415 | isNumber(num) {
416 | if (typeof num == "number" && !isNaN(num)) {
417 | return true;
418 | }
419 | return false;
420 | },
421 | /**
422 | * 多个数的加法运算
423 | */
424 | add(...values) {
425 | return values.reduce((num, value) => {
426 | let r1 = 0;
427 | let r2 = 0;
428 | let m = 0;
429 | try {
430 | r1 = num.toString().split(".")[1].length;
431 | } catch (e) {
432 | }
433 | try {
434 | r2 = value.toString().split(".")[1].length;
435 | } catch (e) {
436 | }
437 | m = Math.pow(10, Math.max(r1, r2));
438 | return (num * m + value * m) / m;
439 | });
440 | },
441 | /**
442 | * 多个数的减法运算
443 | */
444 | subtract(...values) {
445 | return values.reduce((num, value) => {
446 | let r1 = 0;
447 | let r2 = 0;
448 | let m = 0;
449 | try {
450 | r1 = num.toString().split(".")[1].length;
451 | } catch (e) {
452 | }
453 | try {
454 | r2 = value.toString().split(".")[1].length;
455 | } catch (e) {
456 | }
457 | m = Math.pow(10, Math.max(r1, r2));
458 | return (num * m - value * m) / m;
459 | });
460 | },
461 | /**
462 | * 多个数的乘法运算
463 | */
464 | mutiply(...values) {
465 | return values.reduce((num, value) => {
466 | let m = 0;
467 | let s1 = num.toString();
468 | let s2 = value.toString();
469 | try {
470 | m += s1.split(".")[1].length;
471 | } catch (e) {
472 | }
473 | try {
474 | m += s2.split(".")[1].length;
475 | } catch (e) {
476 | }
477 | return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
478 | });
479 | },
480 | /**
481 | * 多个数的除法运算
482 | */
483 | divide(...values) {
484 | return values.reduce((num, value) => {
485 | let t1 = 0;
486 | let t2 = 0;
487 | let s1 = num.toString();
488 | let s2 = value.toString();
489 | try {
490 | t1 = s1.split(".")[1].length;
491 | } catch (e) {
492 | }
493 | try {
494 | t2 = s2.split(".")[1].length;
495 | } catch (e) {
496 | }
497 | return Number(s1.replace(".", "")) / Number(s2.replace(".", "")) * Math.pow(10, t2 - t1);
498 | });
499 | }
500 | };
501 | const element = {
502 | /**
503 | * 判断是否是Window对象
504 | * @param {Object} data 入参
505 | */
506 | isWindow(data2) {
507 | return data2 && data2 instanceof Window;
508 | },
509 | /**
510 | * 获取元素距离某个定位祖先元素左侧/顶部/底部/右侧的距离
511 | * @param {Object} el 元素
512 | * @param {Object} root 定位父元素或者祖先元素,未指定则为document.body
513 | */
514 | getElementPoint(el, root) {
515 | if (!this.isElement(root)) {
516 | root = document.body;
517 | }
518 | if (!this.isContains(root, el)) {
519 | throw new Error("The second argument and the first argument have no hierarchical relationship");
520 | }
521 | const obj = el;
522 | let offsetTop = 0;
523 | let offsetLeft = 0;
524 | while (this.isElement(el) && this.isContains(root, el) && root !== el) {
525 | offsetTop += el.offsetTop;
526 | offsetLeft += el.offsetLeft;
527 | el = el.offsetParent;
528 | }
529 | let offsetRight = root.offsetWidth - offsetLeft - obj.offsetWidth;
530 | let offsetBottom = root.offsetHeight - offsetTop - obj.offsetHeight;
531 | return {
532 | top: offsetTop,
533 | left: offsetLeft,
534 | right: offsetRight,
535 | bottom: offsetBottom
536 | };
537 | },
538 | /**
539 | * 判断某个元素是否包含指定元素,包含相等关系和父子关系
540 | * @param {Object} parentNode 父元素或祖先元素
541 | * @param {Object} childNode 子元素
542 | */
543 | isContains(parentNode, childNode) {
544 | if (parentNode === childNode) {
545 | return true;
546 | }
547 | if (parentNode.contains) {
548 | return parentNode.contains(childNode);
549 | }
550 | if (parentNode.compareDocumentPosition) {
551 | return !!(parentNode.compareDocumentPosition(childNode) & 16);
552 | }
553 | return false;
554 | },
555 | /**
556 | * 判断某个元素是否是指定元素的父元素
557 | * @param {Object} parentNode 父元素
558 | * @param {Object} childNode 子元素
559 | */
560 | isParentNode(parentNode, childNode) {
561 | if (parentNode === childNode) {
562 | return false;
563 | }
564 | return childNode.parentNode === parentNode;
565 | },
566 | /**
567 | * 查找某个元素下指定选择器的子元素
568 | * @param {Object} el 元素
569 | * @param {Object} selector 支持多选择器,等同于querySelectorAll的参数
570 | */
571 | children(el, selector) {
572 | const elements = Array.from(el.querySelectorAll(selector ?? "*"));
573 | return Array.from(elements).filter((elm) => this.isParentNode(el, elm));
574 | },
575 | /**
576 | * 查找某个元素下指定选择器的兄弟元素
577 | * @param {Object} el 元素
578 | * @param {Object} selector 取值等同于queryselectorAll的参数,支持多选择器
579 | */
580 | siblings(el, selector) {
581 | if (!el.parentNode) {
582 | return [];
583 | }
584 | const elements = Array.from(el.parentNode.querySelectorAll(selector ?? "*"));
585 | return elements.filter((elm) => elm.parentNode === el.parentNode && elm != el);
586 | },
587 | /**
588 | * rem与px单位转换
589 | * @param {Object} num rem数值
590 | */
591 | rem2px(num) {
592 | const fs = this.getCssStyle(document.documentElement, "font-size");
593 | return number.mutiply(num, parseFloat(fs));
594 | },
595 | /**
596 | * rem与px单位转换
597 | * @param {Object} num px数值
598 | */
599 | px2rem(num) {
600 | const fs = this.getCssStyle(document.documentElement, "font-size");
601 | return number.divide(num, parseFloat(fs));
602 | },
603 | /**
604 | * 获取元素的内容宽度,内容宽度不包括border和padding
605 | * @param {Object} el 支持css选择器字符串,未指定则表示document.body
606 | */
607 | width(el) {
608 | if (typeof el == "string" && el) {
609 | el = document.body.querySelector(el);
610 | }
611 | if (!this.isElement(el)) {
612 | el = document.body;
613 | }
614 | const clientWidth = el.clientWidth;
615 | const paddingLeft_width = parseFloat(this.getCssStyle(el, "padding-left"));
616 | const paddingRight_width = parseFloat(this.getCssStyle(el, "padding-right"));
617 | return number.subtract(clientWidth, paddingLeft_width, paddingRight_width);
618 | },
619 | /**
620 | * 获取元素的内容高度,内容高度不包括border和padding
621 | * @param {Object} el 支持css选择器字符串 未指定则表示document.body
622 | */
623 | height(el) {
624 | if (typeof el == "string" && el) {
625 | el = document.body.querySelector(el);
626 | }
627 | if (!this.isElement(el)) {
628 | el = document.body;
629 | }
630 | const clientHeight = el.clientHeight;
631 | const paddingTop_height = parseFloat(this.getCssStyle(el, "padding-top"));
632 | const paddingBottom_height = parseFloat(this.getCssStyle(el, "padding-bottom"));
633 | return number.subtract(clientHeight, paddingTop_height, paddingBottom_height);
634 | },
635 | /**
636 | * 移除class
637 | * @param {Object} el 元素
638 | * @param {Object} className 支持多类,以空格划分
639 | */
640 | removeClass(el, className) {
641 | const classArray = string.trim(className).split(/\s+/);
642 | classArray.forEach((item) => {
643 | el.classList.remove(item);
644 | });
645 | },
646 | /**
647 | * 添加class
648 | * @param {Object} el 元素
649 | * @param {Object} className 支持多类,以空格划分
650 | */
651 | addClass(el, className) {
652 | const classArray = string.trim(className).split(/\s+/);
653 | classArray.forEach((item) => {
654 | el.classList.add(item);
655 | });
656 | },
657 | /**
658 | * 判断指定元素是否含有指定类名
659 | * @param {Object} el 元素
660 | * @param {Object} className 支持多类,以空格划分
661 | */
662 | hasClass(el, className) {
663 | const classArray = string.trim(className).split(/\s+/);
664 | return classArray.every((item) => {
665 | return el.classList.contains(item);
666 | });
667 | },
668 | /**
669 | * 监听元素滚动到顶部或者底部
670 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
671 | * @param {Object} callback 回调函数
672 | */
673 | scrollTopBottomTrigger(el, callback) {
674 | if (typeof el == "string" && el) {
675 | el = document.body.querySelector(el);
676 | }
677 | let scrollEle = window;
678 | if (this.isElement(el) && el != document.body && el != document.documentElement) {
679 | scrollEle = el;
680 | }
681 | if (typeof el == "function") {
682 | callback = el;
683 | }
684 | let flag = true;
685 | scrollEle.addEventListener("scroll", () => {
686 | if (this.getScrollTop(scrollEle) <= 0) {
687 | const options = {
688 | state: "top",
689 | target: scrollEle
690 | };
691 | if (!flag) {
692 | return;
693 | }
694 | if (typeof callback == "function") {
695 | flag = false;
696 | callback(options);
697 | }
698 | } else {
699 | const options = {
700 | state: "bottom",
701 | target: scrollEle
702 | };
703 | let height = 0;
704 | if (scrollEle == window) {
705 | height = window.innerHeight;
706 | } else {
707 | height = scrollEle.clientHeight;
708 | }
709 | if (number.add(this.getScrollTop(scrollEle), height) + 1 >= this.getScrollHeight(scrollEle) && height != this.getScrollHeight(scrollEle)) {
710 | if (!flag) {
711 | return;
712 | }
713 | if (typeof callback == "function") {
714 | flag = false;
715 | callback(options);
716 | }
717 | } else {
718 | flag = true;
719 | }
720 | }
721 | });
722 | },
723 | /**
724 | * 获取文档或元素的总宽度
725 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档
726 | */
727 | getScrollWidth(el) {
728 | if (typeof el == "string" && el) {
729 | el = document.body.querySelector(el);
730 | }
731 | let scrollWidth = 0;
732 | if (this.isElement(el) && el != document.documentElement && el != document.body) {
733 | scrollWidth = el.scrollWidth;
734 | } else {
735 | if (document.documentElement.scrollWidth == 0 || document.body.scrollWidth == 0) {
736 | scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
737 | } else {
738 | scrollWidth = document.documentElement.scrollWidth > document.body.scrollWidth ? document.documentElement.scrollWidth : document.body.scrollWidth;
739 | }
740 | }
741 | return scrollWidth;
742 | },
743 | /**
744 | * 获取文档或者元素的总高度
745 | * @param {Object} el 支持css选择器字符串 未指定则表示整个页面文档
746 | */
747 | getScrollHeight(el) {
748 | if (typeof el == "string" && el) {
749 | el = document.body.querySelector(el);
750 | }
751 | let scrollHeight = 0;
752 | if (this.isElement(el) && el != document.documentElement && el != document.body) {
753 | scrollHeight = el.scrollHeight;
754 | } else {
755 | if (document.documentElement.scrollHeight == 0 || document.body.scrollHeight == 0) {
756 | scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
757 | } else {
758 | scrollHeight = document.documentElement.scrollHeight > document.body.scrollHeight ? document.documentElement.scrollHeight : document.body.scrollHeight;
759 | }
760 | }
761 | return scrollHeight;
762 | },
763 | /**
764 | * 设置滚动条在Y轴上的距离
765 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动
766 | */
767 | setScrollTop(options) {
768 | let isWindow = false;
769 | let el = options.el;
770 | if (typeof el == "string" && el) {
771 | el = document.body.querySelector(el);
772 | }
773 | const number$1 = options.number || 0;
774 | const time = options.time || 0;
775 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) {
776 | isWindow = true;
777 | }
778 | return new Promise((resolve) => {
779 | if (time <= 0) {
780 | if (isWindow) {
781 | document.documentElement.scrollTop = document.body.scrollTop = number$1;
782 | } else {
783 | el.scrollTop = number$1;
784 | }
785 | resolve();
786 | } else {
787 | const spacingTime = 10;
788 | let spacingIndex = number.divide(time, spacingTime);
789 | let nowTop = this.getScrollTop(el);
790 | const everTop = number.divide(number.subtract(number$1, nowTop), spacingIndex);
791 | const scrollTimer = setInterval(() => {
792 | if (spacingIndex > 0) {
793 | spacingIndex--;
794 | if (isWindow) {
795 | document.documentElement.scrollTop = document.body.scrollTop = nowTop = number.add(nowTop, everTop);
796 | } else {
797 | el.scrollTop = nowTop = number.add(nowTop, everTop);
798 | }
799 | } else {
800 | clearInterval(scrollTimer);
801 | resolve();
802 | }
803 | }, spacingTime);
804 | }
805 | });
806 | },
807 | /**
808 | * 获取滚动条在Y轴上滚动的距离
809 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
810 | */
811 | getScrollTop(el) {
812 | if (typeof el == "string" && el) {
813 | el = document.body.querySelector(el);
814 | }
815 | let scrollTop = 0;
816 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) {
817 | scrollTop = el.scrollTop;
818 | } else {
819 | if (document.documentElement.scrollTop == 0 || document.body.scrollTop == 0) {
820 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
821 | } else {
822 | scrollTop = document.documentElement.scrollTop > document.body.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
823 | }
824 | }
825 | return scrollTop;
826 | },
827 | /**
828 | * 获取滚动条在X轴上滚动的距离
829 | * @param {Object} el 支持css选择器字符串 未指定则为窗口滚动
830 | */
831 | getScrollLeft(el) {
832 | if (typeof el == "string" && el) {
833 | el = document.body.querySelector(el);
834 | }
835 | let scrollLeft = 0;
836 | if (this.isElement(el) && el != document.body && el != document.documentElement && el != window) {
837 | scrollLeft = el.scrollLeft;
838 | } else {
839 | if (document.documentElement.scrollLeft == 0 || document.body.scrollLeft == 0) {
840 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
841 | } else {
842 | scrollLeft = document.documentElement.scrollLeft > document.body.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft;
843 | }
844 | }
845 | return scrollLeft;
846 | },
847 | /**
848 | * 设置滚动条在X轴上的距离
849 | * @param {Object} options {el,number,time} el支持css选择器字符串 未指定则为窗口滚动
850 | */
851 | setScrollLeft(options) {
852 | let isWindow = false;
853 | let el = options.el;
854 | if (typeof el == "string" && el) {
855 | el = document.body.querySelector(el);
856 | }
857 | const number$1 = options.number || 0;
858 | const time = options.time || 0;
859 | if (!this.isElement(el) || el == document.body || el == document.documentElement || el == window) {
860 | isWindow = true;
861 | }
862 | return new Promise((resolve) => {
863 | if (time <= 0) {
864 | if (isWindow) {
865 | document.documentElement.scrollLeft = document.body.scrollLeft = number$1;
866 | } else {
867 | el.scrollLeft = number$1;
868 | }
869 | resolve();
870 | } else {
871 | const spacingTime = 10;
872 | let spacingIndex = number.divide(time, spacingTime);
873 | let nowLeft = this.getScrollLeft(el);
874 | const everLeft = number.divide(number.subtract(number$1, nowLeft), spacingIndex);
875 | let scrollTimer = setInterval(() => {
876 | if (spacingIndex > 0) {
877 | spacingIndex--;
878 | if (isWindow) {
879 | document.documentElement.scrollLeft = document.body.scrollLeft = nowLeft = number.add(nowLeft, everLeft);
880 | } else {
881 | el.scrollLeft = nowLeft = number.add(nowLeft, everLeft);
882 | }
883 | } else {
884 | clearInterval(scrollTimer);
885 | resolve();
886 | }
887 | }, spacingTime);
888 | }
889 | });
890 | },
891 | /**
892 | * 获取元素指定样式
893 | * @param {Object} el 元素
894 | * @param {Object} cssName 样式名称
895 | */
896 | getCssStyle(el, cssName) {
897 | let cssText = "";
898 | if (document.defaultView && document.defaultView.getComputedStyle) {
899 | cssText = document.defaultView.getComputedStyle(el)[cssName];
900 | } else {
901 | cssText = el.currentStyle[cssName];
902 | }
903 | return cssText;
904 | },
905 | /**
906 | * 判断字符串属于哪种选择器
907 | * @param {Object} selector
908 | */
909 | getCssSelector(selector) {
910 | if (/^#{1}/.test(selector)) {
911 | return {
912 | type: "id",
913 | value: selector.substring(1)
914 | };
915 | }
916 | if (/^\./.test(selector)) {
917 | return {
918 | type: "class",
919 | value: selector.substring(1)
920 | };
921 | }
922 | if (/^\[(.+)\]$/.test(selector)) {
923 | let value = "";
924 | const attribute = string.trim(selector, true).substring(1, string.trim(selector, true).length - 1);
925 | const arry = attribute.split("=");
926 | if (arry.length == 1) {
927 | value = arry[0];
928 | }
929 | if (arry.length == 2) {
930 | value = {
931 | attributeName: arry[0],
932 | attributeValue: arry[1].replace(/\'/g, "").replace(/\"/g, "")
933 | //去除属性值的单引号或者双引号
934 | };
935 | }
936 | return {
937 | type: "attribute",
938 | value
939 | };
940 | }
941 | return {
942 | type: "tag",
943 | value: selector
944 | };
945 | },
946 | /**
947 | * 获取元素距离可视窗口的位置
948 | * @param {Object} el 支持css选择器字符串 未指定则为document.body
949 | */
950 | getElementBounding(el) {
951 | if (typeof el == "string" && el) {
952 | el = document.body.querySelector(el);
953 | }
954 | if (!this.isElement(el)) {
955 | el = document.body;
956 | }
957 | const point = el.getBoundingClientRect();
958 | const top = point.top;
959 | const bottom = number.subtract(document.documentElement.clientHeight || window.innerHeight, point.bottom);
960 | const left = point.left;
961 | const right = number.subtract(document.documentElement.clientWidth || window.innerWidth, point.right);
962 | return {
963 | top,
964 | bottom,
965 | left,
966 | right
967 | };
968 | },
969 | /**
970 | * 判断是否是元素
971 | * @param {Object} el
972 | */
973 | isElement(el) {
974 | return !!el && el instanceof Node && el.nodeType === 1;
975 | },
976 | /**
977 | * 字符串转dom
978 | * @param {Object} html
979 | */
980 | string2dom(html) {
981 | const template = document.createElement("template");
982 | template.innerHTML = html;
983 | if (template.content.children.length == 1) {
984 | return template.content.children[0];
985 | }
986 | return Array.from(template.content.children);
987 | }
988 | };
989 | const parseEventName = (eventName) => {
990 | const eventNames = eventName.split(/[\s]+/g);
991 | const result = [];
992 | eventNames.forEach((name) => {
993 | const arr = name.split(".");
994 | const obj = {
995 | eventName: arr[0]
996 | };
997 | if (arr.length > 1) {
998 | obj.guid = arr[1];
999 | }
1000 | result.push(obj);
1001 | });
1002 | return result;
1003 | };
1004 | const updateEvents = (events) => {
1005 | const obj = {};
1006 | const keys = Object.keys(events);
1007 | keys.forEach((key) => {
1008 | if (events[key]) {
1009 | obj[key] = events[key];
1010 | }
1011 | });
1012 | return obj;
1013 | };
1014 | const bindSingleListener = (el, eventName, guid, fn, options) => {
1015 | const events = data.get(el, "dap-defined-events") || {};
1016 | if (!guid) {
1017 | guid = data.get(el, "dap-event-guid") || 0;
1018 | data.set(el, "dap-event-guid", guid + 1);
1019 | }
1020 | guid = eventName + "." + guid;
1021 | if (events[guid] && events[guid].type == eventName) {
1022 | el.removeEventListener(eventName, events[guid].fn, events[guid].options);
1023 | }
1024 | el.addEventListener(eventName, fn, options);
1025 | events[guid] = {
1026 | type: eventName,
1027 | fn,
1028 | options
1029 | };
1030 | data.set(el, "dap-defined-events", events);
1031 | };
1032 | const unbindSingleListener = (el, eventName, guid) => {
1033 | const events = data.get(el, "dap-defined-events") || {};
1034 | const keys = Object.keys(events);
1035 | const length = keys.length;
1036 | for (let i = 0; i < length; i++) {
1037 | const key = keys[i];
1038 | if (events[key] && events[key].type == eventName) {
1039 | if (guid) {
1040 | if (key == eventName + "." + guid) {
1041 | el.removeEventListener(events[key].type, events[key].fn, events[key].options);
1042 | events[key] = void 0;
1043 | }
1044 | } else {
1045 | el.removeEventListener(events[key].type, events[key].fn, events[key].options);
1046 | events[key] = void 0;
1047 | }
1048 | }
1049 | }
1050 | data.set(el, "dap-defined-events", updateEvents(events));
1051 | };
1052 | const event = {
1053 | /**
1054 | * 绑定事件
1055 | * @param {Object} el 元素节点
1056 | * @param {Object} eventName 事件名称
1057 | * @param {Object} fn 函数
1058 | * @param {Object} options 参数
1059 | */
1060 | on(el, eventName, fn, options) {
1061 | const result = parseEventName(eventName);
1062 | result.forEach((res) => {
1063 | bindSingleListener(el, res.eventName, res.guid, fn.bind(el), options);
1064 | });
1065 | },
1066 | /**
1067 | * 事件解绑
1068 | * @param {Object} el 元素节点
1069 | * @param {Object} eventName 事件名称
1070 | */
1071 | off(el, eventName) {
1072 | const events = data.get(el, "dap-defined-events");
1073 | if (!events) {
1074 | return;
1075 | }
1076 | if (!eventName) {
1077 | const keys = Object.keys(events);
1078 | const length = keys.length;
1079 | for (let i = 0; i < length; i++) {
1080 | const key = keys[i];
1081 | el.removeEventListener(events[key].type, events[key].fn, events[key].options);
1082 | }
1083 | data.remove(el, "dap-defined-events");
1084 | data.remove(el, "dap-event-guid");
1085 | return;
1086 | }
1087 | const result = parseEventName(eventName);
1088 | result.forEach((res) => {
1089 | unbindSingleListener(el, res.eventName, res.guid);
1090 | });
1091 | },
1092 | /**
1093 | * 获取绑定的所有事件
1094 | * @param {*} el
1095 | */
1096 | get(el) {
1097 | const events = data.get(el, "dap-defined-events");
1098 | if (!events) {
1099 | return;
1100 | }
1101 | return events;
1102 | }
1103 | };
1104 | const file = {
1105 | /**
1106 | * 根据文件获取本地可预览的图片路径
1107 | * @param {Object} file
1108 | */
1109 | getImageUrl(file2) {
1110 | return window.URL.createObjectURL(file2);
1111 | },
1112 | /**
1113 | * 将JS的file对象转为BASE64位字符串,通过then方法回调,参数为base64字符串
1114 | * @param {Object} file
1115 | */
1116 | dataFileToBase64(file2) {
1117 | return new Promise((resolve) => {
1118 | const reader = new FileReader();
1119 | reader.readAsDataURL(file2);
1120 | reader.onloadend = () => {
1121 | const dataURL = reader.result;
1122 | resolve(dataURL);
1123 | };
1124 | });
1125 | },
1126 | /**
1127 | * 将base64位格式文件转换为file对象
1128 | * @param {Object} base64String base64位格式字符串
1129 | * @param {Object} fileName 转换后的文件名字,包含后缀
1130 | */
1131 | dataBase64toFile(base64String, fileName) {
1132 | const arr = base64String.split(",");
1133 | const mime = arr[0].match(/:(.*?);/)[1];
1134 | const bstr = atob(arr[1]);
1135 | let n = bstr.length;
1136 | const u8arr = new Uint8Array(n);
1137 | while (n--) {
1138 | u8arr[n] = bstr.charCodeAt(n);
1139 | }
1140 | return new File([u8arr], fileName, {
1141 | type: mime
1142 | });
1143 | },
1144 | /**
1145 | * 图片压缩方法
1146 | * @param {*} file 需要压缩的图片File文件
1147 | * @param {*} options 压缩参数
1148 | */
1149 | compressImage(file2, options) {
1150 | const createFile = (canvas, fileName, quality) => {
1151 | let url = canvas.toDataURL("image/" + (options.mimeType ?? "jpeg"), quality);
1152 | let file3 = this.dataBase64toFile(url, fileName);
1153 | if (options.maxSize && options.maxSize > 0 && file3.size > options.maxSize * 1024) {
1154 | quality = quality <= 0 ? 0 : Number((quality - 0.01).toFixed(2));
1155 | const res = createFile(canvas, fileName, quality);
1156 | url = res.url;
1157 | file3 = res.file;
1158 | quality = res.quality;
1159 | }
1160 | return {
1161 | file: file3,
1162 | url,
1163 | quality
1164 | };
1165 | };
1166 | return new Promise((resolve, reject) => {
1167 | const reader = new FileReader();
1168 | reader.readAsDataURL(file2);
1169 | reader.onload = () => {
1170 | const url = reader.result;
1171 | const img = new Image();
1172 | img.src = url;
1173 | img.onload = () => {
1174 | if (options.minSize && options.minSize > 0 && file2.size <= options.minSize * 1024) {
1175 | resolve({
1176 | file: file2,
1177 | url,
1178 | quality: 1,
1179 | width: img.width,
1180 | height: img.height
1181 | });
1182 | return;
1183 | }
1184 | const canvas = document.createElement("canvas");
1185 | const context = canvas.getContext("2d");
1186 | canvas.width = options.width || img.width;
1187 | canvas.height = options.width ? options.width / (img.width / img.height) : img.height;
1188 | context.drawImage(img, 0, 0, canvas.width, canvas.height);
1189 | const index2 = file2.name.lastIndexOf(".");
1190 | const fileName = file2.name.substring(0, index2) + "." + (options.mimeType ?? "jpeg");
1191 | let res = createFile(canvas, fileName, options.quality ?? 0.8);
1192 | resolve({
1193 | ...res,
1194 | width: canvas.width,
1195 | height: canvas.height
1196 | });
1197 | };
1198 | img.onerror = () => {
1199 | reject(new Error("Failed to load image file"));
1200 | };
1201 | };
1202 | reader.onerror = () => {
1203 | reject(new Error("Failed to load image file"));
1204 | };
1205 | });
1206 | }
1207 | };
1208 | const platform = {
1209 | //设备语言类型
1210 | language() {
1211 | return window.navigator.language;
1212 | },
1213 | /**
1214 | * 获取设备类型
1215 | */
1216 | device() {
1217 | const userAgent = window.navigator.userAgent;
1218 | return {
1219 | PC: !userAgent.match(/AppleWebKit.*Mobile.*/),
1220 | //是否移动终端
1221 | Mobile: !!userAgent.match(/AppleWebKit.*Mobile.*/),
1222 | //是否iPhone
1223 | iPhone: userAgent.includes("iPhone"),
1224 | //是否手机
1225 | Phone: userAgent.includes("Android") && /(?:Mobile)/.test(userAgent) || userAgent.includes("iPhone") || /(?:Windows Phone)/.test(userAgent),
1226 | //是否iPad
1227 | iPad: userAgent.includes("iPad"),
1228 | //是否平板电脑
1229 | Tablet: userAgent.includes("iPad") || userAgent.includes("Android") && !/(?:Mobile)/.test(userAgent) || userAgent.includes("Firefox") && /(?:Tablet)/.test(userAgent),
1230 | //windows手机
1231 | WindowsPhone: /(?:Windows Phone)/.test(userAgent)
1232 | };
1233 | },
1234 | /**
1235 | * 获取浏览器类型
1236 | */
1237 | browser() {
1238 | const userAgent = window.navigator.userAgent;
1239 | return {
1240 | //是否edge浏览器
1241 | Edge: !!userAgent.match(/Edg\/([\d.]+)/),
1242 | //是否微信内置浏览器
1243 | Weixin: userAgent.includes("MicroMessenger"),
1244 | //是否QQ内置浏览器
1245 | QQ: userAgent.includes("QQ"),
1246 | //是否QQ浏览器
1247 | QQBrowser: userAgent.includes("MQQBrowser"),
1248 | //是否UC浏览器
1249 | UC: userAgent.includes("UCBrowser"),
1250 | //是否谷歌浏览器
1251 | Chrome: userAgent.includes("Chrome"),
1252 | //是否火狐浏览器
1253 | Firefox: userAgent.includes("Firefox"),
1254 | //是否搜狗浏览器
1255 | Sougou: userAgent.toLocaleLowerCase().includes("se 2.x") || userAgent.toLocaleLowerCase().includes("metasr") || userAgent.toLocaleLowerCase().includes("sogou"),
1256 | //是否safari浏览器
1257 | Safari: userAgent.includes("Safari") && !userAgent.includes("Chrome")
1258 | };
1259 | },
1260 | /**
1261 | * 获取浏览器内核
1262 | */
1263 | browserKernel() {
1264 | const userAgent = window.navigator.userAgent;
1265 | if (userAgent.includes("Presto")) {
1266 | return "opera";
1267 | }
1268 | if (userAgent.includes("AppleWebKit")) {
1269 | return "webkit";
1270 | }
1271 | if (userAgent.includes("Gecko") && !userAgent.includes("KHTML")) {
1272 | return "gecko";
1273 | }
1274 | },
1275 | /**
1276 | * 获取操作系统数据
1277 | */
1278 | os() {
1279 | const userAgent = window.navigator.userAgent;
1280 | return {
1281 | //是否Windows系统
1282 | Windows: userAgent.includes("Windows"),
1283 | //x64/x32
1284 | WindowsCPU: function() {
1285 | if (userAgent.toLocaleLowerCase().includes("win64") || userAgent.toLocaleLowerCase().includes("wow64")) {
1286 | return "x64";
1287 | } else if (userAgent.toLocaleLowerCase().includes("win32") || userAgent.toLocaleLowerCase().includes("wow32")) {
1288 | return "x32";
1289 | }
1290 | }(),
1291 | //Windows版本
1292 | WindowsVersion: function() {
1293 | if (userAgent.includes("Windows NT 6.1") || userAgent.includes("Windows 7")) {
1294 | return "win7";
1295 | }
1296 | if (userAgent.includes("Windows NT 6.3") || userAgent.includes("Windows NT 6.2") || userAgent.includes("Windows NT 8")) {
1297 | return "win8";
1298 | }
1299 | if (userAgent.includes("Windows NT 10") || userAgent.includes("Windows NT 6.4")) {
1300 | return "win10";
1301 | }
1302 | }(),
1303 | //是否Mac
1304 | Mac: userAgent.includes("Macintosh"),
1305 | //Mac版本
1306 | MacVersion: function() {
1307 | if (userAgent.includes("Macintosh")) {
1308 | const matches = userAgent.match(/Mac OS X ([^\s]+)\)/);
1309 | if (matches && matches[1]) {
1310 | return matches[1].split(/_|\./).join(".");
1311 | }
1312 | }
1313 | return "";
1314 | }(),
1315 | //是否ios系统
1316 | ios: !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
1317 | //ios系统版本
1318 | iosVersion: function() {
1319 | if (!!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {
1320 | const matches = userAgent.match(/CPU.+OS ([^\s]+) like Mac OS X/);
1321 | if (matches && matches[1]) {
1322 | return matches[1].split(/_|\./).join(".");
1323 | }
1324 | }
1325 | return "";
1326 | }(),
1327 | //是否Android系统
1328 | Android: userAgent.includes("Android"),
1329 | //Android系统版本
1330 | AndroidVersion: function() {
1331 | const matches = userAgent.match(/Android ([^\s]+);/);
1332 | if (matches && matches[1]) {
1333 | return matches[1].split(/_|\./).join(".");
1334 | }
1335 | return "";
1336 | }(),
1337 | //是否Linux系统
1338 | Linux: userAgent.includes("Linux"),
1339 | //是否鸿蒙系统
1340 | HarmonyOS: userAgent.includes("HarmonyOS"),
1341 | //是否Ubuntu系统
1342 | Ubuntu: userAgent.includes("Ubuntu")
1343 | };
1344 | }
1345 | };
1346 | const speech = {
1347 | /**
1348 | * 将文字加入语音播报队列
1349 | * @param {Object} text
1350 | * @param {Object} options
1351 | */
1352 | start(text, options) {
1353 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
1354 | throw new Error("The current browser does not support this syntax");
1355 | }
1356 | const speech2 = new SpeechSynthesisUtterance();
1357 | speech2.text = text;
1358 | speech2.pitch = (options == null ? void 0 : options.pitch) ?? 0.8;
1359 | speech2.rate = (options == null ? void 0 : options.rate) ?? 1;
1360 | speech2.volume = (options == null ? void 0 : options.volume) ?? 1;
1361 | speech2.lang = "zh-CN";
1362 | speech2.onstart = (event2) => {
1363 | var _a;
1364 | (_a = options == null ? void 0 : options.start) == null ? void 0 : _a.apply(speech2, [
1365 | event2,
1366 | {
1367 | text,
1368 | pitch: options.pitch ?? 0.8,
1369 | rate: options.rate ?? 1,
1370 | volume: options.volume ?? 1
1371 | }
1372 | ]);
1373 | };
1374 | speech2.onend = (event2) => {
1375 | var _a;
1376 | (_a = options == null ? void 0 : options.end) == null ? void 0 : _a.apply(speech2, [
1377 | event2,
1378 | {
1379 | text,
1380 | pitch: options.pitch ?? 0.8,
1381 | rate: options.rate ?? 1,
1382 | volume: options.volume ?? 1
1383 | }
1384 | ]);
1385 | };
1386 | speech2.onpause = (event2) => {
1387 | var _a;
1388 | (_a = options == null ? void 0 : options.pause) == null ? void 0 : _a.apply(speech2, [
1389 | event2,
1390 | {
1391 | text,
1392 | pitch: options.pitch ?? 0.8,
1393 | rate: options.rate ?? 1,
1394 | volume: options.volume ?? 1
1395 | }
1396 | ]);
1397 | };
1398 | speech2.onresume = (event2) => {
1399 | var _a;
1400 | (_a = options == null ? void 0 : options.resume) == null ? void 0 : _a.apply(speech2, [
1401 | event2,
1402 | {
1403 | text,
1404 | pitch: options.pitch ?? 0.8,
1405 | rate: options.rate ?? 1,
1406 | volume: options.volume ?? 1
1407 | }
1408 | ]);
1409 | };
1410 | speech2.onerror = (event2) => {
1411 | var _a;
1412 | (_a = options == null ? void 0 : options.error) == null ? void 0 : _a.apply(speech2, [
1413 | event2,
1414 | {
1415 | text,
1416 | pitch: options.pitch ?? 0.8,
1417 | rate: options.rate ?? 1,
1418 | volume: options.volume ?? 1
1419 | }
1420 | ]);
1421 | };
1422 | window.speechSynthesis.speak(speech2);
1423 | },
1424 | /**
1425 | * 停止播报,停止所有播报队列里面的语音
1426 | */
1427 | stop() {
1428 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
1429 | throw new Error("The current browser does not support this syntax");
1430 | }
1431 | window.speechSynthesis.cancel();
1432 | },
1433 | /**
1434 | * 暂停播报
1435 | */
1436 | pause() {
1437 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
1438 | throw new Error("The current browser does not support this syntax");
1439 | }
1440 | window.speechSynthesis.pause();
1441 | },
1442 | /**
1443 | * 恢复暂停的播报
1444 | */
1445 | resume() {
1446 | if (!window.SpeechSynthesisUtterance || !window.speechSynthesis) {
1447 | throw new Error("The current browser does not support this syntax");
1448 | }
1449 | window.speechSynthesis.resume();
1450 | }
1451 | };
1452 | const index = { number, data, element, event, common, color, file, string, platform, speech };
1453 | export {
1454 | color,
1455 | common,
1456 | data,
1457 | index as default,
1458 | element,
1459 | event,
1460 | file,
1461 | number,
1462 | platform,
1463 | speech,
1464 | string
1465 | };
1466 |
--------------------------------------------------------------------------------