Skip to content

Commit b404b83

Browse files
committed
✨ feat: 增加部分公共函数
1 parent f6113ec commit b404b83

File tree

4 files changed

+381
-4
lines changed

4 files changed

+381
-4
lines changed

README.md

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ ui-utils-kit 是一个高效的偏业务前端工具函数库。
2222
- **海报制作**
2323
将 DOM 元素转换为图片(canvas),支持自动下载或返回 Blob 格式数据。
2424
> 针对跨域图片问题,内置 `html2canvas` proxy 解决方案。
25+
- **业务工具类函数**
26+
提供一些常用的业务工具函数,如 JSON 安全解析、敏感信息脱敏等。
2527

2628
---
2729

@@ -45,12 +47,14 @@ ui-utils-kit 的工具函数分为三大类:`tree`、`business`、`common`,
4547
**方式一:**
4648
```javascript
4749
import { tree } from "ui-utils-kit";
50+
4851
const result = tree.buildTree(nodes);
4952
```
5053

5154
**方式二:**
5255
```javascript
5356
import { buildTree } from "ui-utils-kit";
57+
5458
const result = buildTree(nodes);
5559
```
5660

@@ -83,6 +87,9 @@ const result = buildTree(nodes);
8387

8488
**`tree.treeToArr`** 将树形数据转换为扁平化数组,便于遍历与处理。
8589

90+
- **参数:**
91+
- `tree` (`TreeNode`):包含 `id``name``children`的树形结构数据。
92+
- `node` (`TreeNode`):包含 `id``pid` 的节点数据。
8693
- **示例:**
8794
```typescript
8895
import { treeToArr } from 'ui-utils-kit';
@@ -96,6 +103,8 @@ const result = buildTree(nodes);
96103

97104
**`tree.updateTreeCheckStatus`** 更新树中节点的选中状态(含子节点与父节点联动)。
98105

106+
- **参数:**
107+
- `tree` (`TreeNode`):包含 `id``name``children`的树形结构数据。
99108
- **示例:**
100109
```typescript
101110
import { updateTreeCheckStatus } from 'ui-utils-kit';
@@ -172,7 +181,97 @@ const result = buildTree(nodes);
172181
173182
## 🎨 公共通用函数 (common)
174183
175-
> 当前暂无公共通用函数,后续版本将持续更新。
184+
### 1. safeJsonParse<T>(jsonString: string, defaultValue: T): [Error \| null, T]
185+
186+
**功能描述:** 安全地解析 JSON 字符串,如果解析出错则返回默认值和错误对象。
187+
188+
**参数**
189+
- `jsonString: string` — 要解析的 JSON 格式字符串。
190+
191+
- `defaultValue: T` — 当解析失败时返回的默认值,类型与期待的解析结果相同。
192+
193+
**返回值**
194+
`[Error | null, T]` — 一个元组:
195+
196+
- 示例
197+
```typescript
198+
import { safeJsonParse } from 'ui-utils-kit';
199+
200+
const [err, data] = safeJsonParse('{"foo": 42}', { foo: 0 });
201+
if (err) {
202+
// 处理解析错误
203+
} else {
204+
console.log(data.foo); // 42
205+
}
206+
```
207+
### 2. desensitize(value: string, type: "mobile" | "idcard"): string
208+
**功能描述**:对敏感信息(手机号或身份证号)进行脱敏处理,隐藏中间部分。
209+
210+
**参数**
211+
- `value: string` — 原始字符串,如手机号或身份证号。
212+
- `type: "mobile" \| "idcard"` — 数据类型,"mobile" 脱敏手机号,"idcard" 脱敏身份证号。
213+
214+
**返回值**
215+
string — 脱敏后字符串,如果输入非字符串则返回空字符串。
216+
217+
- 示例
218+
```typescript
219+
import { desensitize } from 'ui-utils-kit';
220+
221+
console.log(desensitize('13812345678', 'mobile')); // 输出:138****5678
222+
console.log(desensitize('110105199001011234', 'idcard')); // 输出:110105********1234
223+
```
224+
225+
### 3. Mutex 类
226+
功能描述:模拟互斥锁机制,用于控制异步操作对共享资源的访问,确保同一时刻只有一个操作进入临界区。
227+
> ##### 以下是一些应用场景
228+
> - 防止按钮重复点击:避免用户多次点击同一按钮导致重复网络请求或状态混乱
229+
> - 强制 API 调用顺序:确保一组异步接口按预期顺序依次执行,防止乱序带来的逻辑错误
230+
> - 多标签页 localStorage 访问:在多个浏览器标签或窗口同时操作同一 localStorage 时,避免数据竞争和丢失
231+
> - 分片上传(Chunked Upload):在大文件上传时,按顺序上传每个分片,确保断点续传或失败重试时不会错乱
232+
> - Web Worker 任务同步:在主线程与 Worker 线程之间同步访问共享内存(如 SharedArrayBuffer )时,保证原子性
233+
234+
235+
- 示例
236+
237+
```typescript
238+
<template>
239+
<view>
240+
<button @click="onSubmit" :disabled="isSubmitting">
241+
{{ isSubmitting ? '提交中...' : '提交' }}
242+
</button>
243+
</view>
244+
</template>
245+
246+
<script setup lang="ts">
247+
import { ref } from 'vue'
248+
import Mutex from '@/utils/Mutex' // 假设 Mutex 存放在 utils 目录
249+
250+
// 状态变量,无需在 setup 中 return,自动暴露给模板使用
251+
const isSubmitting = ref(false)
252+
// 创建一个互斥锁实例
253+
const submitMutex = new Mutex()
254+
255+
// 点击处理函数
256+
const onSubmit = async () => {
257+
// 获取锁:若已有操作在进行,则挂起后续调用
258+
await submitMutex.lock()
259+
try {
260+
isSubmitting.value = true
261+
// 模拟网络请求
262+
await new Promise<void>(resolve => setTimeout(resolve, 1500))
263+
uni.showToast({ title: '提交成功' })
264+
} catch (err: any) {
265+
uni.showModal({ title: '错误', content: err.message })
266+
} finally {
267+
isSubmitting.value = false
268+
// 释放锁,允许下一次点击
269+
submitMutex.unlock()
270+
}
271+
}
272+
</script>
273+
274+
```
176275
177276
---
178277

packages/core/README.md

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,89 @@ const result = buildTree(nodes);
172172
173173
## 🎨 公共通用函数 (common)
174174
175-
> 当前暂无公共通用函数,后续版本将持续更新。
175+
### 1. safeJsonParse<T>(jsonString: string, defaultValue: T): [Error \| null, T]
176+
177+
**功能描述:**
178+
安全地解析 JSON 字符串,如果解析出错则返回默认值和错误对象。
179+
180+
```typescript
181+
export function safeJsonParse<T>(jsonString: string, defaultValue: T): [Error | null, T] {
182+
try {
183+
const parsed = JSON.parse(jsonString) as T;
184+
return [null, parsed];
185+
} catch (error) {
186+
console.error("JSON 解析错误:", error);
187+
return [error instanceof Error ? error : new Error(String(error)), defaultValue];
188+
}
189+
}
190+
```
191+
**参数**
192+
- `jsonString: string` — 要解析的 JSON 格式字符串。
193+
194+
- `defaultValue: T` — 当解析失败时返回的默认值,类型与期待的解析结果相同。
195+
196+
**返回值**
197+
`[Error | null, T]` — 一个元组:
198+
199+
- 示例
200+
```typescript
201+
const [err, data] = safeJsonParse('{"foo": 42}', { foo: 0 });
202+
if (err) {
203+
// 处理解析错误
204+
} else {
205+
console.log(data.foo); // 42
206+
}
207+
```
208+
### 2. desensitize(value: string, type: "mobile" | "idcard"): string
209+
功能描述:对敏感信息(手机号或身份证号)进行脱敏处理,隐藏中间部分。
210+
211+
**参数**
212+
- `value: string` — 原始字符串,如手机号或身份证号。
213+
214+
- `type: "mobile" \| "idcard"` — 数据类型,"mobile" 脱敏手机号,"idcard" 脱敏身份证号。
215+
216+
**返回值**
217+
string — 脱敏后字符串,如果输入非字符串则返回空字符串。
218+
- 示例
219+
```typescript
220+
console.log(desensitize('13812345678', 'mobile')); // 输出:138****5678
221+
console.log(desensitize('110105199001011234', 'idcard')); // 输出:110105********1234
222+
```
223+
224+
### 3. Mutex 类
225+
功能描述:模拟互斥锁机制,用于控制异步操作对共享资源的访问,确保同一时刻只有一个操作进入临界区。
226+
- 示例
227+
228+
```typescript
229+
import Mutex from './Mutex';
230+
231+
const mutex = new Mutex();
232+
233+
// 初始状态
234+
console.log('初始状态', mutex.isLocked(), mutex.queueLength()); // 初始状态 false 0
235+
236+
// 获取锁
237+
await mutex.lock();
238+
console.log('获取锁后', mutex.isLocked(), mutex.queueLength()); // 获取锁后 true 0
239+
240+
// 第二次请求锁,不会立即获取,加入队列
241+
const pending = mutex.lock().then(() => {
242+
console.log('第二次获取锁', mutex.isLocked(), mutex.queueLength());
243+
});
244+
console.log('请求队列长度', mutex.queueLength()); // 请求队列长度 1
245+
246+
// 执行临界区代码
247+
// ...
248+
249+
// 释放锁,自动唤醒队列中的下一个请求
250+
mutex.unlock();
251+
console.log('释放锁后', mutex.isLocked(), mutex.queueLength()); // 释放锁后 true 0
252+
253+
await pending; // 等待第二个请求获取锁
254+
// 最后释放锁
255+
mutex.unlock();
256+
console.log('全部完成', mutex.isLocked(), mutex.queueLength()); // 全部完成 false 0
257+
```
176258
177259
---
178260

packages/core/src/common.ts

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,118 @@
22
* =====================
33
* =======公共函数=======
44
* =====================
5-
*/
5+
*/
6+
7+
8+
/**
9+
* 安全地解析 JSON 字符串。
10+
*
11+
* @param jsonString - 要解析的 JSON 字符串。
12+
* @param defaultValue - 解析失败时返回的默认值。
13+
* @returns 一个元组,第一个元素为错误对象(若无错误则为 null),第二个元素为解析结果或默认值。
14+
*/
15+
export function safeJsonParse<T>(jsonString: string, defaultValue: T): [Error | null, T] {
16+
try {
17+
const parsed = JSON.parse(jsonString) as T;
18+
return [null, parsed];
19+
} catch (error) {
20+
console.error("JSON 解析错误:", error);
21+
return [error instanceof Error ? error : new Error(String(error)), defaultValue];
22+
}
23+
}
24+
25+
/**
26+
* 对敏感信息进行脱敏处理
27+
*
28+
* @param value - 原始字符串(如手机号、身份证号)
29+
* @param type - 数据类型,可选值为 "mobile"(手机号)或 "idcard"(身份证号)
30+
* @returns 脱敏后的字符串
31+
*/
32+
export function desensitize(value: string, type: "mobile" | "idcard"): string {
33+
if (typeof value !== "string") {
34+
return "";
35+
}
36+
37+
switch (type) {
38+
case "mobile":
39+
// 手机号脱敏:保留前3位和后4位,中间4位替换为星号
40+
// 示例:13812345678 => 138****5678
41+
return value.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
42+
43+
case "idcard":
44+
// 身份证号脱敏:保留前6位和后4位,中间位数替换为星号
45+
// 示例:110105199001011234 => 110105********1234
46+
return value.replace(/^(\d{6})\d+(\d{4})$/, (_, p1, p2) => {
47+
const middleLength = value.length - p1.length - p2.length;
48+
const masked = "*".repeat(middleLength);
49+
return `${p1}${masked}${p2}`;
50+
});
51+
52+
default:
53+
return value;
54+
}
55+
}
56+
57+
/**
58+
* 模拟互斥锁(Mutex)机制,用于控制异步操作对共享资源的访问
59+
*/
60+
export default class Mutex {
61+
62+
/**
63+
* 锁的状态:true 表示已锁定,false 表示未锁定
64+
*/
65+
#locked = false;
66+
67+
/**
68+
* 等待获取锁的 Promise resolve 函数队列
69+
*/
70+
#queue: Array<() => void> = [];
71+
72+
/**
73+
* 获取锁。如果当前已锁定,则将请求加入队列,等待解锁后依次获取。
74+
* @returns Promise<void> - 当获取到锁时,Promise 被 resolve。
75+
*/
76+
public lock(): Promise<void> {
77+
if (this.#locked) {
78+
// 已锁定,将请求加入队列
79+
return new Promise((resolve) => {
80+
this.#queue.push(resolve);
81+
});
82+
}
83+
84+
// 未锁定,立即获取锁
85+
this.#locked = true;
86+
return Promise.resolve();
87+
}
88+
89+
/**
90+
* 释放锁。如果有等待队列,则唤醒队列中的下一个请求;否则将锁状态设为未锁定。
91+
*/
92+
public unlock(): void {
93+
if (this.#queue.length > 0) {
94+
// 唤醒队列中的下一个请求
95+
const nextResolve = this.#queue.shift();
96+
nextResolve?.();
97+
} else {
98+
// 没有等待请求,释放锁
99+
this.#locked = false;
100+
}
101+
}
102+
103+
/**
104+
* 查询当前锁的状态
105+
* @returns boolean - true 表示已锁定,false 表示未锁定
106+
*/
107+
public isLocked(): boolean {
108+
return this.#locked;
109+
}
110+
111+
/**
112+
* 获取当前等待队列的长度
113+
* @returns number - 等待队列中的请求数量
114+
*/
115+
public queueLength(): number {
116+
return this.#queue.length;
117+
}
118+
119+
}

0 commit comments

Comments
 (0)