zcw-shared 2.1.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -180
- package/dist/constants/apiErrorCodes.d.ts +17 -0
- package/dist/constants/apiErrorCodes.js +26 -0
- package/dist/constants/apiErrorCodes.js.map +1 -0
- package/dist/constants/authStorage.d.ts +4 -0
- package/dist/constants/authStorage.js +5 -0
- package/dist/constants/authStorage.js.map +1 -0
- package/dist/functions/im/formatImChatConversationListTime.d.ts +1 -0
- package/dist/functions/im/formatImChatConversationListTime.js +19 -0
- package/dist/functions/im/formatImChatConversationListTime.js.map +1 -0
- package/dist/functions/image/getImageDimensions.js +15 -5
- package/dist/functions/image/getImageDimensions.js.map +1 -1
- package/dist/functions/storage/useDexieShortcuts.d.ts +28 -0
- package/dist/functions/storage/useDexieShortcuts.js +54 -0
- package/dist/functions/storage/useDexieShortcuts.js.map +1 -0
- package/dist/functions/storage/useLocalStorage.js +11 -12
- package/dist/functions/storage/useLocalStorage.js.map +1 -1
- package/dist/functions/storage/useSessionStorage.js +11 -12
- package/dist/functions/storage/useSessionStorage.js.map +1 -1
- package/dist/functions/storage/useStorageWithIndexedDB.js +4 -5
- package/dist/functions/storage/useStorageWithIndexedDB.js.map +1 -1
- package/dist/schemas/auth.schema.d.ts +4 -4
- package/dist/vue-hooks/browser/useBridgeMessage.js +7 -8
- package/dist/vue-hooks/browser/useBridgeMessage.js.map +1 -1
- package/package.json +25 -1
- package/types/address.d.ts +38 -0
- package/types/im-api.d.ts +118 -0
- package/types/im-chat.d.ts +49 -0
- package/types/notification.d.ts +37 -0
- package/types/oss.d.ts +26 -0
- package/types/page-config.d.ts +14 -0
- package/types/payment.d.ts +21 -0
- package/types/performance.d.ts +12 -0
- package/types/rbac.d.ts +24 -0
- package/types/report.d.ts +29 -0
- package/types/review.d.ts +13 -0
package/README.md
CHANGED
|
@@ -38,7 +38,8 @@ src/
|
|
|
38
38
|
├── schemas/ # Zod Schema 定义(用于运行时验证)
|
|
39
39
|
│ ├── auth.schema.ts # 认证相关 Schema
|
|
40
40
|
│ └── video.schema.ts # 视频相关 Schema
|
|
41
|
-
|
|
41
|
+
├── vue-hooks/ # Vue 组合式 API(依赖注入 env)
|
|
42
|
+
└── reactive/ # 跨框架响应式辅助(可选)
|
|
42
43
|
|
|
43
44
|
types/ # 应用层类型定义
|
|
44
45
|
├── auth.d.ts # 认证相关类型
|
|
@@ -53,14 +54,13 @@ references/ # 环境API类型声明
|
|
|
53
54
|
├── dom.d.ts # DOM API
|
|
54
55
|
└── ...
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
└── tests/ # 测试用例
|
|
57
|
+
docs/ # VitePress 文档站(含 API 与浏览器端交互演示组件)
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
### 架构层次关系
|
|
61
61
|
|
|
62
62
|
```
|
|
63
|
-
应用层 (functions/ + hooks/)
|
|
63
|
+
应用层 (functions/ + vue-hooks/ + reactive/)
|
|
64
64
|
↓ 依赖注入
|
|
65
65
|
环境抽象层 (references/)
|
|
66
66
|
↓ 类型约束
|
|
@@ -173,7 +173,8 @@ import type { Ref } from '../../references/vue.d' // Vue 没有 references 文
|
|
|
173
173
|
- 必须组合 `useStorage` 与具体适配器(如 `useLocalStorage`、`useSessionStorage`、`useStorageWithIndexedDB`)创建 `storage` 对象
|
|
174
174
|
- Env 类型统一定义为 `ReturnType<typeof useStorage<string>>`(或自定义 value 类型)
|
|
175
175
|
- 在实现中仅使用 `storage.get/set/remove/clear` 等 Promise API,避免混用同步方法
|
|
176
|
-
-
|
|
176
|
+
- 文档站中的 VitePress 交互示例同样需要使用 `useStorage` 与适配器,确保行为与生产代码一致
|
|
177
|
+
- **多表结构化 IndexedDB**(多 object store、复合索引、`transaction`)可使用 **`useDexieShortcuts`**:将 `Dexie` 构造器与 `indexedDB` 注入 `deps`,应用侧安装 peer 依赖 **`dexie`**(版本范围见 `package.json` 的 `peerDependencies`)。详见 `docs/api/storage/useDexieShortcuts.md`。
|
|
177
178
|
|
|
178
179
|
#### 规则 5:每个函数维护自己完整的依赖,不要分散
|
|
179
180
|
|
|
@@ -471,161 +472,11 @@ import { COLOR_VALIDATION_PATTERNS } from 'zcw-shared/constants/colorPatterns'
|
|
|
471
472
|
import { LoginResponseSchema, VideoSchema } from 'zcw-shared/schemas/auth.schema'
|
|
472
473
|
import { VideoListResponseSchema } from 'zcw-shared/schemas/video.schema'
|
|
473
474
|
|
|
474
|
-
//
|
|
475
|
-
import
|
|
475
|
+
// 导入存储抽象(具体路径以 package.json exports 为准)
|
|
476
|
+
import useStorage from 'zcw-shared/functions/storage/useStorage'
|
|
476
477
|
```
|
|
477
478
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
`playground` 通过 workspace 引用 `zcw-shared`:
|
|
481
|
-
|
|
482
|
-
```json
|
|
483
|
-
// playground/package.json
|
|
484
|
-
{
|
|
485
|
-
"dependencies": {
|
|
486
|
-
"zcw-shared": "workspace:*" // 引用本地workspace
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
在测试中直接导入:
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
// playground/tests/convertColor.test.ts
|
|
495
|
-
import { convertColor } from 'zcw-shared/functions/color/convertColor'
|
|
496
|
-
import type { RgbColor } from 'zcw-shared/types/color'
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
## Playground 测试指南
|
|
500
|
-
|
|
501
|
-
### 测试环境特点
|
|
502
|
-
|
|
503
|
-
- **Workspace 引用**:直接使用 `shared` 包名导入
|
|
504
|
-
- **独立依赖**:可安装测试专用的依赖包
|
|
505
|
-
- **TypeScript 支持**:完整的类型检查和智能提示
|
|
506
|
-
- **实时测试**:修改函数后立即验证
|
|
507
|
-
|
|
508
|
-
### 测试文件结构
|
|
509
|
-
|
|
510
|
-
每个函数都应该有对应的测试文件:
|
|
511
|
-
|
|
512
|
-
```
|
|
513
|
-
playground/tests/
|
|
514
|
-
├── convertColor.test.ts # 颜色转换测试
|
|
515
|
-
├── colorValidation.test.ts # 颜色验证测试
|
|
516
|
-
├── extractColors.test.ts # 颜色提取测试
|
|
517
|
-
├── modifyGradle.test.ts # Android构建测试
|
|
518
|
-
└── ...
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### 测试用例编写规范
|
|
522
|
-
|
|
523
|
-
#### 1. 基础功能测试
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
import { convertColor } from 'zcw-shared/functions/color/convertColor'
|
|
527
|
-
import type { ColorFormat } from 'zcw-shared/types/color'
|
|
528
|
-
|
|
529
|
-
// 基础测试用例
|
|
530
|
-
const testCases = [
|
|
531
|
-
{ input: '#ff0000', target: 'rgb' as ColorFormat, expected: 'rgb(255, 0, 0)' },
|
|
532
|
-
{ input: 'rgb(255, 0, 0)', target: 'hex' as ColorFormat, expected: '#ff0000' },
|
|
533
|
-
// 错误输入测试
|
|
534
|
-
{ input: 'invalid-color', target: 'hex' as ColorFormat, expected: null },
|
|
535
|
-
]
|
|
536
|
-
|
|
537
|
-
function runBasicTests() {
|
|
538
|
-
testCases.forEach((testCase, index) => {
|
|
539
|
-
const result = convertColor(testCase.input, testCase.target)
|
|
540
|
-
const passed = result === testCase.expected
|
|
541
|
-
console.log(passed ? '✅' : '❌', `测试 ${index + 1}:`, testCase.input, '->', result)
|
|
542
|
-
})
|
|
543
|
-
}
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
#### 2. 边界值测试
|
|
547
|
-
|
|
548
|
-
```typescript
|
|
549
|
-
// 边界值和异常情况
|
|
550
|
-
const edgeCases = [
|
|
551
|
-
{ input: '#000000', target: 'rgb', expected: 'rgb(0, 0, 0)' },
|
|
552
|
-
{ input: '#ffffff', target: 'rgb', expected: 'rgb(255, 255, 255)' },
|
|
553
|
-
{ input: 'rgb(300, 0, 0)', target: 'hex', expected: null }, // 超出范围
|
|
554
|
-
]
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
#### 3. 环境依赖测试
|
|
558
|
-
|
|
559
|
-
**重要说明**:Playground测试运行在Node.js环境中,可以直接使用Node.js的原生API和第三方库。
|
|
560
|
-
|
|
561
|
-
- **Node.js环境**:可以直接使用`fs`、`path`、`crypto`等Node.js API
|
|
562
|
-
- **第三方库**:可以安装和导入npm包进行测试
|
|
563
|
-
- **类型兼容**:如遇类型不兼容,可使用`fs as any`等方式处理
|
|
564
|
-
- **特殊环境模拟**:只有无法在Node.js中运行的环境(如微信`wx`、浏览器特定API)才需要模拟
|
|
565
|
-
|
|
566
|
-
对于需要特殊环境模拟的函数:
|
|
567
|
-
|
|
568
|
-
```typescript
|
|
569
|
-
import { buildProject } from 'zcw-shared/functions/android/buildProject'
|
|
570
|
-
|
|
571
|
-
// 创建模拟的依赖函数
|
|
572
|
-
const mockDeps = {
|
|
573
|
-
existsSync: (path: string) => path.includes('build.gradle'),
|
|
574
|
-
readFileSync: (path: string) => 'mock file content',
|
|
575
|
-
readdirSync: (path: string) => ['file1.txt', 'file2.txt'],
|
|
576
|
-
statSync: (path: string) => ({ isDirectory: () => false, isFile: () => true }),
|
|
577
|
-
join: (...paths: string[]) => paths.join('/'),
|
|
578
|
-
platform: 'darwin',
|
|
579
|
-
exec: (cmd: string, callback: any) => callback(null, 'success', ''),
|
|
580
|
-
setTimeout: (fn: () => void, delay: number) => global.setTimeout(fn, delay)
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function testBuildProject() {
|
|
584
|
-
const result = buildProject(
|
|
585
|
-
{ projectPath: '/mock/project', buildVariant: 'debug' },
|
|
586
|
-
mockDeps
|
|
587
|
-
)
|
|
588
|
-
console.log('构建结果:', result)
|
|
589
|
-
}
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
#### 4. 类型安全测试
|
|
593
|
-
|
|
594
|
-
```typescript
|
|
595
|
-
// 测试类型定义的正确性
|
|
596
|
-
function testTypes() {
|
|
597
|
-
const rgbColor: RgbColor = { r: 255, g: 0, b: 0 }
|
|
598
|
-
const result = convertColor(rgbColor, 'hex')
|
|
599
|
-
|
|
600
|
-
// TypeScript 应该能正确推断类型
|
|
601
|
-
if (result) {
|
|
602
|
-
console.log('转换结果:', result.toUpperCase()) // string 方法
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
### 运行测试
|
|
608
|
-
|
|
609
|
-
```bash
|
|
610
|
-
# 进入 playground 目录
|
|
611
|
-
cd playground
|
|
612
|
-
|
|
613
|
-
# 运行单个测试
|
|
614
|
-
npx tsx tests/convertColor.test.ts
|
|
615
|
-
|
|
616
|
-
# 运行所有测试
|
|
617
|
-
npx tsx tests/*.test.ts
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
### 测试覆盖要求
|
|
621
|
-
|
|
622
|
-
每个函数的测试应该包含:
|
|
623
|
-
|
|
624
|
-
1. **正常输入**:典型使用场景
|
|
625
|
-
2. **边界值**:最小值、最大值、临界点
|
|
626
|
-
3. **异常输入**:null、undefined、错误类型
|
|
627
|
-
4. **环境兼容性**:不同环境下的行为
|
|
628
|
-
5. **性能测试**:大数据量或复杂场景
|
|
479
|
+
本地校验以 **`pnpm exec tsc --noEmit`**、**`pnpm run build`** 与 **`pnpm run docs:build`**(文档站)为准;交互演示统一在 **`docs/`** 内维护,说明见 **`docs/guide/documentation.md`**(本站内路径为 `/guide/documentation`)。
|
|
629
480
|
|
|
630
481
|
## 开发工作流程
|
|
631
482
|
|
|
@@ -633,11 +484,9 @@ npx tsx tests/*.test.ts
|
|
|
633
484
|
|
|
634
485
|
**重要提醒:**
|
|
635
486
|
|
|
636
|
-
1.
|
|
487
|
+
1. **文档同步更新**:每次新增或修改函数后,**必须**同步更新 VitePress 文档。详细规范见下方「文档规范」章节。
|
|
637
488
|
|
|
638
|
-
2.
|
|
639
|
-
|
|
640
|
-
3. **文档同步更新**:每次新增或修改函数后,**必须**同步更新 VitePress 文档。详细规范见下方"文档规范"章节。
|
|
489
|
+
2. **构建验证**:改动后在本包根目录执行 `pnpm exec tsc --noEmit` 与 `pnpm run build`,确保类型与导出正确。
|
|
641
490
|
|
|
642
491
|
### 1. 添加新函数
|
|
643
492
|
|
|
@@ -648,22 +497,19 @@ src/functions/category/newFunction.ts
|
|
|
648
497
|
# 2. 定义类型(如需要)
|
|
649
498
|
types/category.d.ts
|
|
650
499
|
|
|
651
|
-
# 3.
|
|
652
|
-
playground/tests/newFunction.test.ts
|
|
653
|
-
|
|
654
|
-
# 4. 创建函数文档(必须!)
|
|
500
|
+
# 3. 创建函数文档(必须!)
|
|
655
501
|
docs/api/category/newFunction.md
|
|
656
502
|
|
|
657
|
-
#
|
|
503
|
+
# 4. 创建 Playground 组件(如果是浏览器端函数)
|
|
658
504
|
docs/.vitepress/components/NewFunctionPlayground.vue
|
|
659
505
|
|
|
660
|
-
#
|
|
506
|
+
# 5. 更新侧边栏配置
|
|
661
507
|
docs/.vitepress/config.ts
|
|
662
508
|
|
|
663
|
-
#
|
|
509
|
+
# 6. 验证文档编译
|
|
664
510
|
npm run docs:dev
|
|
665
511
|
|
|
666
|
-
#
|
|
512
|
+
# 7. 构建和导出
|
|
667
513
|
npm run build
|
|
668
514
|
```
|
|
669
515
|
|
|
@@ -809,8 +655,7 @@ export function useNewHook<T>(
|
|
|
809
655
|
- [ ] 错误处理完善,不会意外抛出异常
|
|
810
656
|
- [ ] 有完整的 JSDoc 文档注释
|
|
811
657
|
|
|
812
|
-
#### ✅
|
|
813
|
-
- [ ] 创建了对应的测试文件
|
|
658
|
+
#### ✅ 文档检查
|
|
814
659
|
- [ ] 创建了 VitePress 文档
|
|
815
660
|
- [ ] 浏览器函数创建了 Playground 组件
|
|
816
661
|
- [ ] 更新了侧边栏配置
|
|
@@ -862,13 +707,6 @@ npm run publish:major # 主要版本
|
|
|
862
707
|
4. **接口命名规范**:`{FunctionName}Deps`,例如 `BuildProjectDeps`、`ModifyManifestDeps`
|
|
863
708
|
5. **使用 deps. 前缀**:所有依赖访问都使用 `deps.methodName()`,不解构
|
|
864
709
|
|
|
865
|
-
### 测试要求
|
|
866
|
-
|
|
867
|
-
1. **全面覆盖**:正常、边界、异常情况
|
|
868
|
-
2. **环境模拟**:使用mock对象测试环境依赖
|
|
869
|
-
3. **文档化**:测试即文档,展示函数的使用方法
|
|
870
|
-
4. **依赖注入测试**:验证函数在不同依赖注入下的行为
|
|
871
|
-
|
|
872
710
|
### 快速检查清单
|
|
873
711
|
|
|
874
712
|
在编写新函数或修改现有函数时,快速检查以下要点:
|
|
@@ -890,7 +728,6 @@ npm run publish:major # 主要版本
|
|
|
890
728
|
**✅ 文档完整性:**
|
|
891
729
|
- [ ] Markdown 文档(`docs/api/category/functionName.md`)
|
|
892
730
|
- [ ] Playground 组件(浏览器函数必须)
|
|
893
|
-
- [ ] 测试文件(`playground/tests/functionName.test.ts`)
|
|
894
731
|
- [ ] 侧边栏配置已更新
|
|
895
732
|
|
|
896
733
|
### 常见错误和解决方案
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ApiErrorResponse {
|
|
2
|
+
statusCode: number;
|
|
3
|
+
message?: string | string[];
|
|
4
|
+
path?: string;
|
|
5
|
+
timestamp?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const API_HTTP_STATUS: {
|
|
8
|
+
readonly NO_CONTENT: 204;
|
|
9
|
+
readonly BAD_REQUEST: 400;
|
|
10
|
+
readonly UNAUTHORIZED: 401;
|
|
11
|
+
readonly FORBIDDEN: 403;
|
|
12
|
+
readonly NOT_FOUND: 404;
|
|
13
|
+
readonly INTERNAL_SERVER_ERROR: 500;
|
|
14
|
+
readonly SERVICE_UNAVAILABLE: 503;
|
|
15
|
+
};
|
|
16
|
+
export declare const API_ERROR_MESSAGES: Record<number, string>;
|
|
17
|
+
export declare function getApiErrorMessage(statusCode: number, body?: ApiErrorResponse | null): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const API_HTTP_STATUS = {
|
|
2
|
+
NO_CONTENT: 204,
|
|
3
|
+
BAD_REQUEST: 400,
|
|
4
|
+
UNAUTHORIZED: 401,
|
|
5
|
+
FORBIDDEN: 403,
|
|
6
|
+
NOT_FOUND: 404,
|
|
7
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
8
|
+
SERVICE_UNAVAILABLE: 503,
|
|
9
|
+
};
|
|
10
|
+
export const API_ERROR_MESSAGES = {
|
|
11
|
+
[API_HTTP_STATUS.BAD_REQUEST]: '请求参数错误',
|
|
12
|
+
[API_HTTP_STATUS.UNAUTHORIZED]: '未授权,请重新登录',
|
|
13
|
+
[API_HTTP_STATUS.FORBIDDEN]: '没有权限访问此资源',
|
|
14
|
+
[API_HTTP_STATUS.NOT_FOUND]: '请求的资源不存在',
|
|
15
|
+
[API_HTTP_STATUS.INTERNAL_SERVER_ERROR]: '服务器错误,请稍后重试',
|
|
16
|
+
[API_HTTP_STATUS.SERVICE_UNAVAILABLE]: '服务暂不可用,请稍后重试',
|
|
17
|
+
};
|
|
18
|
+
const DEFAULT_MESSAGE = '请求失败';
|
|
19
|
+
export function getApiErrorMessage(statusCode, body) {
|
|
20
|
+
if (body?.message != null) {
|
|
21
|
+
const msg = body.message;
|
|
22
|
+
return Array.isArray(msg) ? msg[0] ?? DEFAULT_MESSAGE : msg;
|
|
23
|
+
}
|
|
24
|
+
return API_ERROR_MESSAGES[statusCode] ?? DEFAULT_MESSAGE;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=apiErrorCodes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiErrorCodes.js","sourceRoot":"","sources":["../../src/constants/apiErrorCodes.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,GAAG;IAChB,YAAY,EAAE,GAAG;IACjB,SAAS,EAAE,GAAG;IACd,SAAS,EAAE,GAAG;IACd,qBAAqB,EAAE,GAAG;IAC1B,mBAAmB,EAAE,GAAG;CAChB,CAAA;AAGV,MAAM,CAAC,MAAM,kBAAkB,GAA2B;IACxD,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,QAAQ;IACvC,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,WAAW;IAC3C,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,WAAW;IACxC,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,UAAU;IACvC,CAAC,eAAe,CAAC,qBAAqB,CAAC,EAAE,aAAa;IACtD,CAAC,eAAe,CAAC,mBAAmB,CAAC,EAAE,cAAc;CACtD,CAAA;AAED,MAAM,eAAe,GAAG,MAAM,CAAA;AAK9B,MAAM,UAAU,kBAAkB,CAChC,UAAkB,EAClB,IAA8B;IAE9B,IAAI,IAAI,EAAE,OAAO,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAA;QACxB,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,GAAG,CAAA;IAC7D,CAAC;IACD,OAAO,kBAAkB,CAAC,UAAU,CAAC,IAAI,eAAe,CAAA;AAC1D,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const AUTH_STORAGE_KEY_ACCESS = 'auth:access';
|
|
2
|
+
export const AUTH_STORAGE_KEY_REFRESH = 'auth:refresh';
|
|
3
|
+
export const AUTH_STORAGE_KEY_EXPIRES = 'auth:expires';
|
|
4
|
+
export const AUTH_STORAGE_KEY_USER_INFO = 'auth:userInfo';
|
|
5
|
+
//# sourceMappingURL=authStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authStorage.js","sourceRoot":"","sources":["../../src/constants/authStorage.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAA;AAGpD,MAAM,CAAC,MAAM,wBAAwB,GAAG,cAAc,CAAA;AAGtD,MAAM,CAAC,MAAM,wBAAwB,GAAG,cAAc,CAAA;AAGtD,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatImChatConversationListTime(timestamp?: Date): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function formatImChatConversationListTime(timestamp) {
|
|
2
|
+
if (!timestamp)
|
|
3
|
+
return '';
|
|
4
|
+
const now = new Date();
|
|
5
|
+
const diff = now.getTime() - timestamp.getTime();
|
|
6
|
+
const minutes = Math.floor(diff / (1000 * 60));
|
|
7
|
+
const hours = Math.floor(diff / (1000 * 60 * 60));
|
|
8
|
+
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
9
|
+
if (minutes < 1)
|
|
10
|
+
return '刚刚';
|
|
11
|
+
if (minutes < 60)
|
|
12
|
+
return `${minutes}分钟前`;
|
|
13
|
+
if (hours < 24)
|
|
14
|
+
return `${hours}小时前`;
|
|
15
|
+
if (days < 7)
|
|
16
|
+
return `${days}天前`;
|
|
17
|
+
return timestamp.toLocaleDateString();
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=formatImChatConversationListTime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatImChatConversationListTime.js","sourceRoot":"","sources":["../../../src/functions/im/formatImChatConversationListTime.ts"],"names":[],"mappings":"AACA,MAAM,UAAU,gCAAgC,CAAC,SAAgB;IAC/D,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAA;IAEzB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,CAAA;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAA;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;IAErD,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAC5B,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,KAAK,CAAA;IACxC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,GAAG,KAAK,KAAK,CAAA;IACpC,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,GAAG,IAAI,IAAI,CAAA;IAChC,OAAO,SAAS,CAAC,kBAAkB,EAAE,CAAA;AACvC,CAAC"}
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
export async function getImageDimensions(file, deps) {
|
|
2
2
|
return new Promise((resolve, reject) => {
|
|
3
|
+
const url = deps.createObjectURL(file);
|
|
3
4
|
const img = new deps.Image();
|
|
5
|
+
const revoke = () => {
|
|
6
|
+
try {
|
|
7
|
+
const urlApi = globalThis.URL;
|
|
8
|
+
urlApi?.revokeObjectURL?.(url);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
}
|
|
12
|
+
};
|
|
4
13
|
img.onload = () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
});
|
|
14
|
+
const w = img.naturalWidth;
|
|
15
|
+
const h = img.naturalHeight;
|
|
16
|
+
revoke();
|
|
17
|
+
resolve({ width: w, height: h });
|
|
9
18
|
};
|
|
10
19
|
img.onerror = () => {
|
|
20
|
+
revoke();
|
|
11
21
|
reject(new Error('获取图片尺寸失败'));
|
|
12
22
|
};
|
|
13
|
-
img.src =
|
|
23
|
+
img.src = url;
|
|
14
24
|
});
|
|
15
25
|
}
|
|
16
26
|
//# sourceMappingURL=getImageDimensions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getImageDimensions.js","sourceRoot":"","sources":["../../../src/functions/image/getImageDimensions.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAU,EAAE,IAA4B;IAC/E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAA;QAC5B,GAAG,CAAC,MAAM,GAAG,GAAG,
|
|
1
|
+
{"version":3,"file":"getImageDimensions.js","sourceRoot":"","sources":["../../../src/functions/image/getImageDimensions.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAU,EAAE,IAA4B;IAC/E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;QACtC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAA;QAC5B,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAI,UAA6E,CAAC,GAAG,CAAA;gBACjG,MAAM,EAAE,eAAe,EAAE,CAAC,GAAG,CAAC,CAAA;YAChC,CAAC;YAAC,MAAM,CAAC;YAET,CAAC;QACH,CAAC,CAAA;QACD,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;YAChB,MAAM,CAAC,GAAG,GAAG,CAAC,YAAY,CAAA;YAC1B,MAAM,CAAC,GAAG,GAAG,CAAC,aAAa,CAAA;YAC3B,MAAM,EAAE,CAAA;YACR,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;QAClC,CAAC,CAAA;QACD,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE;YACjB,MAAM,EAAE,CAAA;YACR,MAAM,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAA;QAC/B,CAAC,CAAA;QACD,GAAG,CAAC,GAAG,GAAG,GAAG,CAAA;IACf,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IDBFactory } from '../../../references/indexeddb.d';
|
|
2
|
+
import type { Console } from '../../../references/console.d';
|
|
3
|
+
export type DexieConstructor = typeof import('dexie').default;
|
|
4
|
+
export type DexieDatabase = InstanceType<DexieConstructor>;
|
|
5
|
+
export interface UseDexieShortcutsDeps {
|
|
6
|
+
Dexie: DexieConstructor;
|
|
7
|
+
indexedDB: IDBFactory | null | undefined;
|
|
8
|
+
consoleError: Console['error'];
|
|
9
|
+
}
|
|
10
|
+
export interface DexieSchemaVersionSpec {
|
|
11
|
+
version: number;
|
|
12
|
+
stores: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export interface OpenDexieWithSchemaOptions {
|
|
15
|
+
dbName: string;
|
|
16
|
+
versions: DexieSchemaVersionSpec[];
|
|
17
|
+
}
|
|
18
|
+
export type DexieTransactionMode = 'r' | 'rw' | 'r!';
|
|
19
|
+
export declare function assertIndexedDbAvailable(deps: UseDexieShortcutsDeps): void;
|
|
20
|
+
export declare function openDexieWithSchema(deps: UseDexieShortcutsDeps, options: OpenDexieWithSchemaOptions): Promise<DexieDatabase>;
|
|
21
|
+
export declare function deleteDexieDatabase(deps: UseDexieShortcutsDeps, dbName: string): Promise<void>;
|
|
22
|
+
export declare function runDexieTransaction<T>(deps: UseDexieShortcutsDeps, db: DexieDatabase, mode: DexieTransactionMode, storeNames: string[], run: (trans: unknown) => Promise<T> | T): Promise<T>;
|
|
23
|
+
export default function useDexieShortcuts(deps: UseDexieShortcutsDeps): {
|
|
24
|
+
assertIndexedDbAvailable: () => void;
|
|
25
|
+
openWithSchema: (opts: OpenDexieWithSchemaOptions) => Promise<import("dexie").Dexie>;
|
|
26
|
+
deleteDatabase: (dbName: string) => Promise<void>;
|
|
27
|
+
transaction: <T>(db: DexieDatabase, mode: DexieTransactionMode, storeNames: string[], run: (trans: unknown) => Promise<T> | T) => Promise<T>;
|
|
28
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function assertIndexedDbAvailable(deps) {
|
|
2
|
+
if (deps.indexedDB == null) {
|
|
3
|
+
throw new Error('IndexedDB 不可用:请在 deps 中注入 indexedDB(如 window.indexedDB)');
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export async function openDexieWithSchema(deps, options) {
|
|
7
|
+
assertIndexedDbAvailable(deps);
|
|
8
|
+
if (options.versions.length === 0) {
|
|
9
|
+
throw new Error('openDexieWithSchema: versions 不能为空');
|
|
10
|
+
}
|
|
11
|
+
const db = new deps.Dexie(options.dbName);
|
|
12
|
+
const sorted = [...options.versions].sort((a, b) => a.version - b.version);
|
|
13
|
+
for (const v of sorted) {
|
|
14
|
+
db.version(v.version).stores(v.stores);
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
await db.open();
|
|
18
|
+
return db;
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
deps.consoleError(`[useDexieShortcuts] 打开数据库失败: ${options.dbName}`, e);
|
|
22
|
+
throw e;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function deleteDexieDatabase(deps, dbName) {
|
|
26
|
+
assertIndexedDbAvailable(deps);
|
|
27
|
+
try {
|
|
28
|
+
await deps.Dexie.delete(dbName);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
deps.consoleError(`[useDexieShortcuts] 删除数据库失败: ${dbName}`, e);
|
|
32
|
+
throw e;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function runDexieTransaction(deps, db, mode, storeNames, run) {
|
|
36
|
+
if (storeNames.length === 0) {
|
|
37
|
+
return Promise.reject(new Error('runDexieTransaction: 至少指定一个 object store 名称'));
|
|
38
|
+
}
|
|
39
|
+
return db
|
|
40
|
+
.transaction(mode, storeNames, (trans) => run(trans))
|
|
41
|
+
.catch((e) => {
|
|
42
|
+
deps.consoleError('[useDexieShortcuts] 事务执行失败', e);
|
|
43
|
+
throw e;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export default function useDexieShortcuts(deps) {
|
|
47
|
+
return {
|
|
48
|
+
assertIndexedDbAvailable: () => assertIndexedDbAvailable(deps),
|
|
49
|
+
openWithSchema: (opts) => openDexieWithSchema(deps, opts),
|
|
50
|
+
deleteDatabase: (dbName) => deleteDexieDatabase(deps, dbName),
|
|
51
|
+
transaction: (db, mode, storeNames, run) => runDexieTransaction(deps, db, mode, storeNames, run),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=useDexieShortcuts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDexieShortcuts.js","sourceRoot":"","sources":["../../../src/functions/storage/useDexieShortcuts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export default function useLocalStorage(deps, options = {}) {
|
|
2
|
-
const { localStorage, consoleError } = deps;
|
|
3
2
|
const { prefix = '', serializer } = options;
|
|
4
3
|
const defaultSerializer = {
|
|
5
4
|
serialize: (value) => JSON.stringify(value),
|
|
@@ -16,13 +15,13 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
16
15
|
const get = async (key) => {
|
|
17
16
|
try {
|
|
18
17
|
const fullKey = prefix + key;
|
|
19
|
-
const item = localStorage.getItem(fullKey);
|
|
18
|
+
const item = deps.localStorage.getItem(fullKey);
|
|
20
19
|
if (item === null)
|
|
21
20
|
return null;
|
|
22
21
|
return currentSerializer.deserialize(item);
|
|
23
22
|
}
|
|
24
23
|
catch (error) {
|
|
25
|
-
consoleError(`获取 localStorage 值失败: ${error}`);
|
|
24
|
+
deps.consoleError(`获取 localStorage 值失败: ${error}`);
|
|
26
25
|
return null;
|
|
27
26
|
}
|
|
28
27
|
};
|
|
@@ -30,7 +29,7 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
30
29
|
try {
|
|
31
30
|
const fullKey = prefix + key;
|
|
32
31
|
const serializedValue = currentSerializer.serialize(value);
|
|
33
|
-
localStorage.setItem(fullKey, serializedValue);
|
|
32
|
+
deps.localStorage.setItem(fullKey, serializedValue);
|
|
34
33
|
}
|
|
35
34
|
catch (error) {
|
|
36
35
|
throw new Error(`设置 localStorage 值失败: ${error}`);
|
|
@@ -39,7 +38,7 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
39
38
|
const remove = async (key) => {
|
|
40
39
|
try {
|
|
41
40
|
const fullKey = prefix + key;
|
|
42
|
-
localStorage.removeItem(fullKey);
|
|
41
|
+
deps.localStorage.removeItem(fullKey);
|
|
43
42
|
}
|
|
44
43
|
catch (error) {
|
|
45
44
|
throw new Error(`移除 localStorage 值失败: ${error}`);
|
|
@@ -47,7 +46,7 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
47
46
|
};
|
|
48
47
|
const clear = async () => {
|
|
49
48
|
try {
|
|
50
|
-
localStorage.clear();
|
|
49
|
+
deps.localStorage.clear();
|
|
51
50
|
}
|
|
52
51
|
catch (error) {
|
|
53
52
|
throw new Error(`清空 localStorage 失败: ${error}`);
|
|
@@ -56,8 +55,8 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
56
55
|
const getAll = async () => {
|
|
57
56
|
try {
|
|
58
57
|
const result = {};
|
|
59
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
60
|
-
const key = localStorage.key(i);
|
|
58
|
+
for (let i = 0; i < deps.localStorage.length; i++) {
|
|
59
|
+
const key = deps.localStorage.key(i);
|
|
61
60
|
if (key && key.startsWith(prefix)) {
|
|
62
61
|
const actualKey = key.slice(prefix.length);
|
|
63
62
|
const value = await get(actualKey);
|
|
@@ -69,15 +68,15 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
69
68
|
return result;
|
|
70
69
|
}
|
|
71
70
|
catch (error) {
|
|
72
|
-
consoleError(`获取所有 localStorage 值失败: ${error}`);
|
|
71
|
+
deps.consoleError(`获取所有 localStorage 值失败: ${error}`);
|
|
73
72
|
return {};
|
|
74
73
|
}
|
|
75
74
|
};
|
|
76
75
|
const getKeys = async () => {
|
|
77
76
|
try {
|
|
78
77
|
const keys = [];
|
|
79
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
80
|
-
const key = localStorage.key(i);
|
|
78
|
+
for (let i = 0; i < deps.localStorage.length; i++) {
|
|
79
|
+
const key = deps.localStorage.key(i);
|
|
81
80
|
if (key && key.startsWith(prefix)) {
|
|
82
81
|
keys.push(key.slice(prefix.length));
|
|
83
82
|
}
|
|
@@ -85,7 +84,7 @@ export default function useLocalStorage(deps, options = {}) {
|
|
|
85
84
|
return keys;
|
|
86
85
|
}
|
|
87
86
|
catch (error) {
|
|
88
|
-
consoleError(`获取 localStorage 键列表失败: ${error}`);
|
|
87
|
+
deps.consoleError(`获取 localStorage 键列表失败: ${error}`);
|
|
89
88
|
return [];
|
|
90
89
|
}
|
|
91
90
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLocalStorage.js","sourceRoot":"","sources":["../../../src/functions/storage/useLocalStorage.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAC,OAAO,UAAU,eAAe,CACrC,IAAsB,EACtB,UAA0B,EAAE;IAE5B,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"useLocalStorage.js","sourceRoot":"","sources":["../../../src/functions/storage/useLocalStorage.ts"],"names":[],"mappings":"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"}
|