# cross-env-plugins

一个跨环境的 TypeScript/JavaScript 工具库，提供常用的工具函数和验证函数，适用于 Web 和 Node.js 环境。

[![npm version](https://img.shields.io/npm/v/cross-env-plugins.svg)](https://www.npmjs.com/package/cross-env-plugins)
[![license](https://img.shields.io/npm/l/cross-env-plugins.svg)](https://github.com/mzzhref/cross-env-plugins/blob/main/LICENSE)

## 特性

- ✅ **TypeScript 项目** - 完整的类型定义和类型声明文件
- ✅ 支持 CommonJS、ES Module 和 UMD 三种格式
- ✅ 零依赖，轻量级
- ✅ 完整的 TypeScript 类型支持，提供 `.d.ts` 类型声明文件
- ✅ 适用于浏览器和 Node.js 环境
- ✅ 包含 **40+ 工具函数** 和 **22+ 验证函数**
- ✅ 解决 JavaScript 浮点数精度问题（0.1 + 0.2 = 0.3）

## 安装

```bash
npm install cross-env-plugins
```

## 使用方式

### CommonJS

```javascript
const { utils, valids } = require('cross-env-plugins')

console.log(utils.formatTime())
console.log(utils.random(10, 20))
console.log(valids.isMobile('13800000000'))
```

### ES Module

```javascript
import { utils, valids } from 'cross-env-plugins'

console.log(utils.formatTime())
console.log(utils.random(10, 20))
console.log(valids.isMobile('13800000000'))
```

### TypeScript

```typescript
import { utils, valids } from 'cross-env-plugins'

// 完整的类型支持
const time: string = utils.formatTime()
const randomNum: number = utils.random(10, 20)
const isValid: boolean = valids.isMobile('13800000000')
```

### 浏览器 Script 标签

```html
<script src="./dist/cross-env-plugins.umd.js"></script>
<script>
    const { utils, valids } = crossEnvPlugins
    
    console.log(utils.formatTime())
    console.log(utils.random(10, 20))
    console.log(valids.isMobile('13800000000'))
</script>
```

## API 文档

### utils - 工具函数

#### `random(min, max)`
生成指定范围内的随机整数（包含边界值）

**参数：**
- `min` (number): 最小值（左闭区间）
- `max` (number): 最大值（右闭区间）

**返回：** `number` - min 到 max 之间的随机整数

**示例：**
```javascript
utils.random(10, 20) // 返回 10-20 之间的随机整数
```

#### `formatTime(date, format)`
格式化时间

**参数：**
- `date` (Date|string, 可选): 时间对象或字符串，默认为当前时间
- `format` (string, 可选): 格式化字符串，默认为 `'yyyy-MM-dd hh:mm:ss'`

**返回：** `string` - 格式化后的日期字符串

**支持的格式：**
- `yyyy` - 年份
- `MM` - 月份（01-12）
- `dd` - 日期（01-31）
- `hh` - 小时（00-23）
- `mm` - 分钟（00-59）
- `ss` - 秒（00-59）
- `S` - 毫秒

**示例：**
```javascript
utils.formatTime() // '2024-01-01 12:00:00'
utils.formatTime(new Date(), 'yyyy-MM-dd') // '2024-01-01'
utils.formatTime('2024-01-01', 'yyyy年MM月dd日') // '2024年01月01日'
```

#### `getDayOfWeek(date)`
获取日期对应的星期几

**参数：**
- `date` (string|Date): 日期字符串或对象

**返回：** `string` - 星期几（Sunday, Monday, Tuesday...）

**示例：**
```javascript
utils.getDayOfWeek('2023-08-09') // 'Wednesday'
```

#### `getTimeUntilEnd(endTime, startTime)`
计算当前时间距离结束时间的时间戳

**参数：**
- `endTime` (string|Date): 结束时间字符串或Date对象
- `startTime` (string|Date, 可选): 开始时间字符串或Date对象，默认为当前时间

**返回：** `number` - 距离结束时间的时间戳（毫秒），如果已过期则返回 0

**示例：**
```javascript
utils.getTimeUntilEnd('2024-9-30 18:00:00') // 返回距离结束时间的毫秒数
utils.getTimeUntilEnd('2024-9-30 18:00:00', '2024-9-1 00:00:00') // 从指定开始时间计算
```

#### `formatCurrency(amount, decimals)`
金额格式化

**参数：**
- `amount` (number|string): 金额
- `decimals` (number, 可选): 保留的小数位数，默认为 2

**返回：** `string` - 格式化后的金额字符串（带千分位分隔符）

**示例：**
```javascript
utils.formatCurrency(12345.678) // '12,345.68'
utils.formatCurrency(12345.678, 0) // '12,346'
```

#### `convertToChinese(num)`
金额转换为中文大写

**参数：**
- `num` (number): 要转换的金额

**返回：** `string` - 中文大写金额

**示例：**
```javascript
utils.convertToChinese(12345.67) // '壹万贰仟叁佰肆拾伍元陆角柒分'
```

#### `generateRandomCode(length)`
生成随机验证码

**参数：**
- `length` (number, 可选): 验证码长度，默认为 6

**返回：** `string` - 随机验证码（包含大小写字母和数字）

**示例：**
```javascript
utils.generateRandomCode(6) // 'aB1cD2'
utils.generateRandomCode(8) // 'Xy9Z2aBc'
```

#### `randomStr(length)`
生成随机字符串

**参数：**
- `length` (number, 可选): 字符串长度，默认为 16

**返回：** `string` - 随机字符串

**示例：**
```javascript
utils.randomStr() // 16位随机字符串
utils.randomStr(20) // 20位随机字符串
```

#### `debounce(fn, delay)`
防抖函数

**参数：**
- `fn` (Function): 要执行的函数
- `delay` (number): 等待时间（毫秒）

**返回：** `Function` - 返回防抖处理后的函数

**示例：**
```javascript
const handleClick = utils.debounce((e) => {
    console.log('按钮点击')
}, 1000)
```

#### `throttle(fn, delay)`
节流函数

**参数：**
- `fn` (Function): 要执行的函数
- `delay` (number): 限制时间间隔（毫秒）

**返回：** `Function` - 返回节流处理后的函数

**示例：**
```javascript
const handleScroll = utils.throttle(() => {
    console.log('Scroll Event')
}, 200)
```

#### `deepClone(obj)`
深拷贝对象

**参数：**
- `obj` (object): 要拷贝的对象

**返回：** `object` - 拷贝后的新对象

**示例：**
```javascript
const newObj = utils.deepClone(originalObj)
```

#### `mergeObjects(target, source, overwrite)`
对象合并

**参数：**
- `target` (object): 目标对象
- `source` (object): 源对象
- `overwrite` (boolean, 可选): 是否覆盖目标对象中已有的属性，默认为 true

**返回：** `object` - 合并后的对象

**示例：**
```javascript
const mergedObj = utils.mergeObjects(targetObj, sourceObj, false)
```

#### `uniqueArray(arr)`
数组去重

**参数：**
- `arr` (Array): 要去重的数组

**返回：** `Array` - 去重后的数组

**示例：**
```javascript
utils.uniqueArray([1, 2, 2, 3, 4, 4, 5]) // [1, 2, 3, 4, 5]
```

#### `checkPwd(str)`
检测密码强度等级

**参数：**
- `str` (string): 密码字符串

**返回：** `number` - 密码强度等级
- `1`: 密码弱
- `2`: 密码中等
- `3`: 密码强
- `4`: 密码很强

**示例：**
```javascript
utils.checkPwd('123456') // 1
utils.checkPwd('abc123') // 2
utils.checkPwd('Abc123') // 3
utils.checkPwd('Abc123!') // 4
```

#### `getUrlParam(name)`
解析 URL 获取指定参数值

**参数：**
- `name` (string, 可选): 参数名，如果不传则返回所有参数对象

**返回：** `string|object` - 参数值或所有参数对象

**注意：** 支持微信小程序环境

**示例：**
```javascript
// URL: https://example.com?id=123&name=test
utils.getUrlParam('id') // '123'
utils.getUrlParam() // { id: '123', name: 'test' }
```

#### `generateUUID()`
生成UUID

**返回：** `string` - UUID字符串

**示例：**
```javascript
utils.generateUUID() // '550e8400-e29b-41d4-a716-446655440000'
```

#### `capitalize(str)`
首字母大写

**参数：**
- `str` (string): 字符串

**返回：** `string` - 首字母大写的字符串

**示例：**
```javascript
utils.capitalize('hello') // 'Hello'
```

#### `camelCase(str)`
驼峰命名转换（下划线转驼峰）

**参数：**
- `str` (string): 下划线命名的字符串

**返回：** `string` - 驼峰命名的字符串

**示例：**
```javascript
utils.camelCase('user_name') // 'userName'
```

#### `snakeCase(str)`
下划线命名转换（驼峰转下划线）

**参数：**
- `str` (string): 驼峰命名的字符串

**返回：** `string` - 下划线命名的字符串

**示例：**
```javascript
utils.snakeCase('userName') // 'user_name'
```

#### `flattenArray(arr, depth)`
数组扁平化

**参数：**
- `arr` (Array): 要扁平化的数组
- `depth` (number, 可选): 扁平化深度，默认为 Infinity

**返回：** `Array` - 扁平化后的数组

**示例：**
```javascript
utils.flattenArray([1, [2, [3, 4]]]) // [1, 2, 3, 4]
```

#### `groupBy(arr, key)`
数组分组

**参数：**
- `arr` (Array): 要分组的数组
- `key` (Function|string): 分组键（函数或属性名）

**返回：** `Object` - 分组后的对象

**示例：**
```javascript
utils.groupBy([{type: 'a', value: 1}, {type: 'b', value: 2}], 'type')
// { a: [{type: 'a', value: 1}], b: [{type: 'b', value: 2}] }
```

#### `paginate(arr, page, pageSize)`
数组分页

**参数：**
- `arr` (Array): 要分页的数组
- `page` (number, 可选): 页码（从1开始），默认为 1
- `pageSize` (number, 可选): 每页数量，默认为 10

**返回：** `Array` - 分页后的数组

**示例：**
```javascript
utils.paginate([1, 2, 3, 4, 5], 1, 2) // [1, 2]
```

#### `getRelativeTime(date)`
获取相对时间（如：3分钟前、2小时前）

**参数：**
- `date` (string|Date): 日期

**返回：** `string` - 相对时间字符串

**示例：**
```javascript
utils.getRelativeTime(new Date(Date.now() - 3600000)) // '1小时前'
```

#### `getDaysDiff(date1, date2)`
计算两个日期的差值（天数）

**参数：**
- `date1` (string|Date): 日期1
- `date2` (string|Date, 可选): 日期2，默认为当前时间

**返回：** `number` - 相差的天数

**示例：**
```javascript
utils.getDaysDiff('2024-01-01', '2024-01-10') // 9
```

#### `isSameDay(date1, date2)`
判断是否为同一天

**参数：**
- `date1` (string|Date): 日期1
- `date2` (string|Date, 可选): 日期2，默认为当前时间

**返回：** `boolean` - 是否为同一天

**示例：**
```javascript
utils.isSameDay('2024-01-01', '2024-01-01 12:00:00') // true
```

#### `formatFileSize(bytes, decimals)`
格式化文件大小

**参数：**
- `bytes` (number): 字节数
- `decimals` (number, 可选): 小数位数，默认为 2

**返回：** `string` - 格式化后的文件大小

**示例：**
```javascript
utils.formatFileSize(1024) // '1.00 KB'
```

#### `formatNumber(num)`
格式化数字（添加千分位）

**参数：**
- `num` (number|string): 数字

**返回：** `string` - 格式化后的数字字符串

**示例：**
```javascript
utils.formatNumber(1234567) // '1,234,567'
```

#### `toPercent(num, decimals)`
百分比转换

**参数：**
- `num` (number): 数字
- `decimals` (number, 可选): 小数位数，默认为 2

**返回：** `string` - 百分比字符串

**示例：**
```javascript
utils.toPercent(0.1234) // '12.34%'
```

#### `base64Encode(str)`
Base64编码

**参数：**
- `str` (string): 要编码的字符串

**返回：** `string` - Base64编码后的字符串

**示例：**
```javascript
utils.base64Encode('hello') // 'aGVsbG8='
```

#### `base64Decode(str)`
Base64解码

**参数：**
- `str` (string): Base64编码的字符串

**返回：** `string` - 解码后的字符串

**示例：**
```javascript
utils.base64Decode('aGVsbG8=') // 'hello'
```

#### `urlEncode(str)`
URL编码

**参数：**
- `str` (string): 要编码的字符串

**返回：** `string` - URL编码后的字符串

**示例：**
```javascript
utils.urlEncode('hello world') // 'hello%20world'
```

#### `urlDecode(str)`
URL解码

**参数：**
- `str` (string): URL编码的字符串

**返回：** `string` - 解码后的字符串

**示例：**
```javascript
utils.urlDecode('hello%20world') // 'hello world'
```

#### `truncate(str, length, suffix)`
字符串截取（支持中文）

**参数：**
- `str` (string): 字符串
- `length` (number): 截取长度
- `suffix` (string, 可选): 后缀，默认为 '...'

**返回：** `string` - 截取后的字符串

**示例：**
```javascript
utils.truncate('这是一段很长的文字', 5) // '这是一段很...'
```

#### `deepEqual(obj1, obj2)`
对象深度比较

**参数：**
- `obj1` (*): 对象1
- `obj2` (*): 对象2

**返回：** `boolean` - 是否相等

**示例：**
```javascript
utils.deepEqual({a: 1, b: {c: 2}}, {a: 1, b: {c: 2}}) // true
```

#### `getType(value)`
获取值的精确类型（使用 Object.prototype.toString.call）

**参数：**
- `value` (*): 要检查的值

**返回：** `string` - 类型字符串（如：'Object', 'Array', 'String', 'Number', 'Boolean', 'Function', 'Date', 'RegExp', 'Null', 'Undefined' 等）

**示例：**
```javascript
utils.getType([]) // 'Array'
utils.getType({}) // 'Object'
utils.getType('hello') // 'String'
utils.getType(null) // 'Null'
```

#### `isType(value, type)`
验证值是否为指定类型（使用 Object.prototype.toString.call，统一类型验证函数）

**参数：**
- `value` (*): 要验证的值
- `type` (string): 类型名称（支持：'object', 'array', 'string', 'number', 'boolean', 'function', 'date', 'regexp', 'null', 'undefined', 'symbol', 'promise', 'error' 等，不区分大小写）

**返回：** `boolean` - 是否为指定类型

**示例：**
```javascript
utils.isType('a', 'string') // true
utils.isType([], 'array') // true
utils.isType({}, 'object') // true
utils.isType(123, 'number') // true
utils.isType(true, 'boolean') // true
utils.isType(null, 'null') // true
utils.isType(undefined, 'undefined') // true
utils.isType(new Date(), 'date') // true
utils.isType(/test/, 'regexp') // true
utils.isType(() => {}, 'function') // true
```

#### `add(a, b, ...args)`
精确加法（解决浮点数精度问题）

**参数：**
- `a` (number): 加数1
- `b` (number): 加数2
- `...args` (number[], 可选): 更多加数

**返回：** `number` - 精确的加法结果

**示例：**
```javascript
utils.add(0.1, 0.2) // 0.3 (而不是 0.30000000000000004)
utils.add(0.1, 0.2, 0.3) // 0.6
utils.add(1.5, 2.3) // 3.8
```

#### `subtract(a, b, ...args)`
精确减法（解决浮点数精度问题）

**参数：**
- `a` (number): 被减数
- `b` (number): 减数
- `...args` (number[], 可选): 更多减数

**返回：** `number` - 精确的减法结果

**示例：**
```javascript
utils.subtract(0.3, 0.1) // 0.2 (而不是 0.19999999999999998)
utils.subtract(1.0, 0.1, 0.2) // 0.7
utils.subtract(5.5, 2.3) // 3.2
```

#### `multiply(a, b, ...args)`
精确乘法（解决浮点数精度问题）

**参数：**
- `a` (number): 乘数1
- `b` (number): 乘数2
- `...args` (number[], 可选): 更多乘数

**返回：** `number` - 精确的乘法结果

**示例：**
```javascript
utils.multiply(0.1, 3) // 0.3
utils.multiply(0.1, 0.2, 0.3) // 0.006
utils.multiply(2.5, 4) // 10
```

#### `divide(a, b, ...args)`
精确除法（解决浮点数精度问题）

**参数：**
- `a` (number): 被除数
- `b` (number): 除数
- `...args` (number[], 可选): 更多除数

**返回：** `number` - 精确的除法结果

**注意：** 当除数为0时会抛出错误

**示例：**
```javascript
utils.divide(0.3, 0.1) // 3
utils.divide(0.6, 0.2, 0.3) // 10
utils.divide(10, 2.5) // 4
```

### valids - 验证函数

#### `isEmail(email)`
校验邮箱格式

**参数：**
- `email` (string): 要校验的邮箱

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isEmail('example@example.com') // true
valids.isEmail('invalid-email') // false
```

#### `isMobile(mobile)`
校验手机号格式（中国手机号）

**参数：**
- `mobile` (string): 要校验的手机号

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isMobile('13800138000') // true
valids.isMobile('12345678901') // false
```

#### `validateIDCard(idCard)`
身份证校验（支持15位和18位）

**参数：**
- `idCard` (string): 身份证号

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.validateIDCard('110101199003075131') // true
valids.validateIDCard('123456789012345') // false
```

#### `checkCode(idCard)`
校验身份证最后一位校验码是否正确

**参数：**
- `idCard` (string): 18位身份证号

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.checkCode('110101199003075131') // true
```

#### `isValidDate(date)`
验证日期格式是否正确

**参数：**
- `date` (string): 日期字符串（格式：YYYYMMDD）

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isValidDate('20240101') // true
valids.isValidDate('20240230') // false（2月没有30号）
```

#### `convertTo18Digits(idCard15)`
将15位身份证号转换为18位

**参数：**
- `idCard15` (string): 15位身份证号

**返回：** `string` - 18位身份证号

**示例：**
```javascript
valids.convertTo18Digits('123456789012345') // '123456199001234567'
```

#### `isURL(url)`
校验URL格式

**参数：**
- `url` (string): 要校验的URL

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isURL('https://www.example.com') // true
```

#### `isIP(ip)`
校验IP地址格式

**参数：**
- `ip` (string): 要校验的IP地址

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isIP('192.168.1.1') // true
```

#### `isBankCard(cardNo)`
校验银行卡号（Luhn算法）

**参数：**
- `cardNo` (string): 银行卡号

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isBankCard('6228480402637874213') // true
```

#### `isChineseName(name)`
校验中文姓名

**参数：**
- `name` (string): 姓名

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isChineseName('张三') // true
```

#### `isPostalCode(code)`
校验邮政编码（中国）

**参数：**
- `code` (string): 邮政编码

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isPostalCode('100000') // true
```

#### `isLicensePlate(plate)`
校验车牌号（中国）

**参数：**
- `plate` (string): 车牌号

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isLicensePlate('京A12345') // true
```

#### `isUnifiedSocialCreditCode(code)`
校验统一社会信用代码

**参数：**
- `code` (string): 统一社会信用代码

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isUnifiedSocialCreditCode('911100001000000000') // true
```

#### `isNumber(value)`
校验是否为数字

**参数：**
- `value` (string|number): 要校验的值

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isNumber('123') // true
valids.isNumber(123) // true
```

#### `isInteger(value)`
校验是否为整数

**参数：**
- `value` (string|number): 要校验的值

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isInteger('123') // true
valids.isInteger(123.5) // false
```

#### `isDecimal(value)`
校验是否为小数

**参数：**
- `value` (string|number): 要校验的值

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isDecimal('123.45') // true
valids.isDecimal(123) // false
```

#### `isEmpty(value)`
校验是否为空（null、undefined、空字符串、空数组、空对象）

**参数：**
- `value` (*): 要校验的值

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isEmpty('') // true
valids.isEmpty([]) // true
valids.isEmpty({}) // true
```

#### `isLength(str, min, max)`
校验字符串长度

**参数：**
- `str` (string): 字符串
- `min` (number): 最小长度
- `max` (number): 最大长度

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isLength('hello', 3, 10) // true
```

#### `isPositiveInteger(value)`
校验是否为正整数

**参数：**
- `value` (string|number): 要校验的值

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isPositiveInteger('123') // true
```

#### `isNonNegativeInteger(value)`
校验是否为非负整数（包括0）

**参数：**
- `value` (string|number): 要校验的值

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isNonNegativeInteger('0') // true
```

#### `isChinese(str)`
校验是否为中文

**参数：**
- `str` (string): 要校验的字符串

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isChinese('你好') // true
```

#### `isEnglish(str)`
校验是否为英文

**参数：**
- `str` (string): 要校验的字符串

**返回：** `boolean` - 返回校验结果

**示例：**
```javascript
valids.isEnglish('hello') // true
```

## 完整示例

```javascript
import { utils, valids } from 'cross-env-plugins'

// 时间相关
console.log(utils.formatTime()) // '2024-01-01 12:00:00'
console.log(utils.getDayOfWeek('2024-01-01')) // 'Monday'
console.log(utils.getTimeUntilEnd('2024-12-31 23:59:59')) // 倒计时毫秒数

// 随机数
console.log(utils.random(1, 100)) // 1-100 之间的随机数
console.log(utils.generateRandomCode(6)) // 6位随机验证码
console.log(utils.randomStr(20)) // 20位随机字符串

// 金额处理
console.log(utils.formatCurrency(12345.67)) // '12,345.67'
console.log(utils.convertToChinese(12345.67)) // '壹万贰仟叁佰肆拾伍元陆角柒分'

// 防抖和节流
const debouncedFn = utils.debounce(() => console.log('防抖'), 1000)
const throttledFn = utils.throttle(() => console.log('节流'), 1000)

// 对象和数组操作
const cloned = utils.deepClone({ a: 1, b: { c: 2 } })
const merged = utils.mergeObjects({ a: 1 }, { b: 2 })
const unique = utils.uniqueArray([1, 2, 2, 3, 3, 4]) // [1, 2, 3, 4]
const flattened = utils.flattenArray([1, [2, [3, 4]]]) // [1, 2, 3, 4]
const grouped = utils.groupBy([{type: 'a', value: 1}, {type: 'b', value: 2}], 'type')
const paginated = utils.paginate([1, 2, 3, 4, 5], 1, 2) // [1, 2]

// 字符串处理
console.log(utils.generateUUID()) // UUID字符串
console.log(utils.capitalize('hello')) // 'Hello'
console.log(utils.camelCase('user_name')) // 'userName'
console.log(utils.snakeCase('userName')) // 'user_name'
console.log(utils.truncate('这是一段很长的文字', 5)) // '这是一段很...'

// 日期处理
console.log(utils.getRelativeTime(new Date(Date.now() - 3600000))) // '1小时前'
console.log(utils.getDaysDiff('2024-01-01', '2024-01-10')) // 9
console.log(utils.isSameDay('2024-01-01', '2024-01-01 12:00:00')) // true

// 数字和文件处理
console.log(utils.formatNumber(1234567)) // '1,234,567'
console.log(utils.toPercent(0.1234)) // '12.34%'
console.log(utils.formatFileSize(1024)) // '1.00 KB'

// 编码解码
console.log(utils.base64Encode('hello')) // 'aGVsbG8='
console.log(utils.base64Decode('aGVsbG8=')) // 'hello'
console.log(utils.urlEncode('hello world')) // 'hello%20world'
console.log(utils.urlDecode('hello%20world')) // 'hello world'

// 类型验证
console.log(utils.getType([])) // 'Array'
console.log(utils.isType('a', 'string')) // true
console.log(utils.isType([], 'array')) // true
console.log(utils.isType({}, 'object')) // true
console.log(utils.deepEqual({a: 1, b: {c: 2}}, {a: 1, b: {c: 2}})) // true

// 精确数学运算（解决浮点数精度问题）
console.log(utils.add(0.1, 0.2)) // 0.3 (而不是 0.30000000000000004)
console.log(utils.subtract(0.3, 0.1)) // 0.2 (而不是 0.19999999999999998)
console.log(utils.multiply(0.1, 3)) // 0.3
console.log(utils.divide(0.3, 0.1)) // 3

// 验证
console.log(valids.isEmail('test@example.com')) // true
console.log(valids.isMobile('13800138000')) // true
console.log(valids.validateIDCard('110101199003075131')) // true
console.log(valids.isURL('https://www.example.com')) // true
console.log(valids.isIP('192.168.1.1')) // true
console.log(valids.isBankCard('6228480402637874213')) // true
console.log(valids.isChineseName('张三')) // true
console.log(valids.isLicensePlate('京A12345')) // true
console.log(valids.isNumber('123')) // true
console.log(valids.isInteger('123')) // true
console.log(valids.isEmpty('')) // true
console.log(valids.isLength('hello', 3, 10)) // true
console.log(valids.isChinese('你好')) // true
console.log(utils.checkPwd('Abc123!@#')) // 4（密码很强）
```

## 许可证

ISC

## 贡献者

- [soulshow](https://gitee.com/mzzhref) - mzzhref@163.com

## 仓库

- [Gitee](https://gitee.com/mzzhref/cross-env-plugins)
