From 0c1b737325c3d808366d3bbe3d62af9365df8668 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 18 May 2026 16:50:14 +0800 Subject: [PATCH] fix: fix lint && typecheck --- .changeset/element-plus-theme-switch.md | 5 - .changeset/page-auto-content-height.md | 5 - .changeset/small-moons-hunt.md | 5 - .changeset/tree-default-value.md | 5 - .../@core/base/shared/src/cache/README.md | 92 +++++++++---------- .../cache/__tests__/storage-manager.test.ts | 34 +++---- .../base/shared/src/cache/indexeddb-driver.ts | 16 ++-- .../shared/src/cache/local-storage-driver.ts | 12 +-- .../shared/src/cache/memory-storage-driver.ts | 6 +- .../base/shared/src/cache/storage-manager.ts | 10 +- packages/@core/base/shared/src/cache/types.ts | 4 +- .../src/utils/__tests__/state-handler.test.ts | 4 +- .../shared/src/utils/__tests__/util.test.ts | 6 +- .../base/shared/src/utils/state-handler.ts | 4 +- packages/@core/preferences/src/preferences.ts | 8 +- .../common-ui/src/components/page/page.vue | 9 +- .../effects/plugins/src/tiptap/extensions.ts | 67 +++++++------- packages/effects/plugins/src/vxe-table/api.ts | 2 +- .../effects/plugins/src/vxe-table/types.ts | 40 ++++---- .../plugins/src/vxe-table/use-viewed-row.ts | 33 +++---- .../plugins/src/vxe-table/use-vxe-grid.vue | 6 +- .../src/request-client/request-client.test.ts | 24 ++--- .../features/preferences-extension/index.vue | 4 +- 23 files changed, 188 insertions(+), 213 deletions(-) delete mode 100644 .changeset/element-plus-theme-switch.md delete mode 100644 .changeset/page-auto-content-height.md delete mode 100644 .changeset/small-moons-hunt.md delete mode 100644 .changeset/tree-default-value.md diff --git a/.changeset/element-plus-theme-switch.md b/.changeset/element-plus-theme-switch.md deleted file mode 100644 index d0c07d83d..000000000 --- a/.changeset/element-plus-theme-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@vben/layouts": patch ---- - -fix: update primary color when toggling dark/light mode with custom theme diff --git a/.changeset/page-auto-content-height.md b/.changeset/page-auto-content-height.md deleted file mode 100644 index 94b81cc9c..000000000 --- a/.changeset/page-auto-content-height.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@vben/common-ui": patch ---- - -fix: skip fixed footer height in auto-content-height calculation diff --git a/.changeset/small-moons-hunt.md b/.changeset/small-moons-hunt.md deleted file mode 100644 index ab3820103..000000000 --- a/.changeset/small-moons-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@vben/icons": patch ---- - -fix: guard svg icon loading during docs SSR diff --git a/.changeset/tree-default-value.md b/.changeset/tree-default-value.md deleted file mode 100644 index 3b82bf789..000000000 --- a/.changeset/tree-default-value.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@vben-core/shadcn-ui": patch ---- - -fix: preserve tree default value when treeData starts empty diff --git a/packages/@core/base/shared/src/cache/README.md b/packages/@core/base/shared/src/cache/README.md index 5b5dc7cf9..00e4aa509 100644 --- a/packages/@core/base/shared/src/cache/README.md +++ b/packages/@core/base/shared/src/cache/README.md @@ -1,7 +1,6 @@ # Cache 模块 -基于**策略模式**的异步存储管理方案,支持多种存储后端(localStorage、IndexedDB、Memory),提供统一的 API -接口。 +基于**策略模式**的异步存储管理方案,支持多种存储后端(localStorage、IndexedDB、Memory),提供统一的 API 接口。 ## 架构设计 @@ -22,11 +21,11 @@ **分层职责:** -| 层级 | 职责 | -|------------------|----------------------------| +| 层级 | 职责 | +| ---------------- | -------------------------------------------- | | `StorageManager` | 命名空间前缀隔离、TTL 过期检查、统一对外 API | -| `IStorageDriver` | 纯粹的 KV 存取抽象接口 | -| 各 Driver 实现 | 对接具体存储引擎,不感知前缀和 TTL | +| `IStorageDriver` | 纯粹的 KV 存取抽象接口 | +| 各 Driver 实现 | 对接具体存储引擎,不感知前缀和 TTL | --- @@ -35,9 +34,9 @@ ### 基本使用(默认 localStorage) ```typescript -import {StorageManager} from '@vben-core/shared/cache'; +import { StorageManager } from '@vben-core/shared/cache'; -const cache = new StorageManager({prefix: 'myapp'}); +const cache = new StorageManager({ prefix: 'myapp' }); // 使用 IndexedDB //new StorageManager({ driver: new IndexedDBDriver(), prefix: 'app' }); @@ -48,14 +47,14 @@ const cache = new StorageManager({prefix: 'myapp'}); //new StorageManager({ driver: new MemoryStorageDriver(), prefix: 'test' }); // 存储数据 -await cache.setItem('user', {name: '张三', age: 28}); +await cache.setItem('user', { name: '张三', age: 28 }); // 读取数据 const user = await cache.getItem('user'); // => { name: '张三', age: 28 } // 带默认值读取 -const settings = await cache.getItem('settings', {theme: 'light'}); +const settings = await cache.getItem('settings', { theme: 'light' }); // 如果不存在,返回 { theme: 'light' } // 删除数据 @@ -68,7 +67,7 @@ await cache.clear(); ### 带 TTL 过期 ```typescript -const cache = new StorageManager({prefix: 'session'}); +const cache = new StorageManager({ prefix: 'session' }); // 设置 5 分钟后过期(TTL 单位为毫秒) await cache.setItem('token', 'abc123', 5 * 60 * 1000); @@ -191,20 +190,20 @@ const cache = new StorageManager({ new StorageManager(options?: StorageManagerOptions) ``` -| 参数 | 类型 | 默认值 | 说明 | -|----------|------------------|----------------------------|--------------| -| `driver` | `IStorageDriver` | `new LocalStorageDriver()` | 存储驱动实例 | -| `prefix` | `string` | `''` | 键前缀,用于命名空间隔离 | +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `driver` | `IStorageDriver` | `new LocalStorageDriver()` | 存储驱动实例 | +| `prefix` | `string` | `''` | 键前缀,用于命名空间隔离 | #### 方法 -| 方法 | 签名 | 说明 | -|---------------------|-------------------------------------------------------------------------|-------------------| -| `getItem` | `getItem(key: string, defaultValue?: T \| null): Promise` | 获取存储项,过期或不存在返回默认值 | -| `setItem` | `setItem(key: string, value: T, ttl?: number): Promise` | 设置存储项,可选 TTL(毫秒) | -| `removeItem` | `removeItem(key: string): Promise` | 删除指定存储项 | -| `clear` | `clear(): Promise` | 清除当前前缀下所有存储项 | -| `clearExpiredItems` | `clearExpiredItems(): Promise` | 主动清理所有过期项 | +| 方法 | 签名 | 说明 | +| --- | --- | --- | +| `getItem` | `getItem(key: string, defaultValue?: T \| null): Promise` | 获取存储项,过期或不存在返回默认值 | +| `setItem` | `setItem(key: string, value: T, ttl?: number): Promise` | 设置存储项,可选 TTL(毫秒) | +| `removeItem` | `removeItem(key: string): Promise` | 删除指定存储项 | +| `clear` | `clear(): Promise` | 清除当前前缀下所有存储项 | +| `clearExpiredItems` | `clearExpiredItems(): Promise` | 主动清理所有过期项 | --- @@ -233,7 +232,7 @@ interface IStorageDriver { ### 自定义 Driver ```typescript -import type {IStorageDriver} from '@vben-core/shared/cache'; +import type { IStorageDriver } from '@vben-core/shared/cache'; class CookieStorageDriver implements IStorageDriver { async getItem(key: string): Promise { @@ -301,11 +300,11 @@ function createStorageManager(prefix: string) { ```typescript // 不同模块使用不同前缀,互不干扰 -const userCache = new StorageManager({prefix: 'user'}); -const configCache = new StorageManager({prefix: 'config'}); +const userCache = new StorageManager({ prefix: 'user' }); +const configCache = new StorageManager({ prefix: 'config' }); -await userCache.setItem('profile', {name: '张三'}); -await configCache.setItem('profile', {theme: 'dark'}); +await userCache.setItem('profile', { name: '张三' }); +await configCache.setItem('profile', { theme: 'dark' }); // 各自独立 await userCache.getItem('profile'); // => { name: '张三' } @@ -356,24 +355,24 @@ interface StorageItem { 采用**惰性删除 + 主动清理**双重策略: -| 策略 | 触发时机 | 说明 | -|------|--------------------------|---------------------| -| 惰性删除 | 调用 `getItem` 时 | 读取时检查过期,过期则删除并返回默认值 | +| 策略 | 触发时机 | 说明 | +| --- | --- | --- | +| 惰性删除 | 调用 `getItem` 时 | 读取时检查过期,过期则删除并返回默认值 | | 主动清理 | 调用 `clearExpiredItems` 时 | 遍历所有带前缀的 key,删除已过期项 | --- ## 各 Driver 对比 -| 特性 | LocalStorageDriver | IndexedDBDriver | MemoryStorageDriver | -|-------|--------------------|-----------------|---------------------| -| 持久化 | ✅ | ✅ | ❌ | -| 容量 | 5-10 MB | 数百 MB+ | 受内存限制 | -| 速度 | 快(同步) | 中等(异步 I/O) | 最快 | -| 数据类型 | 仅 JSON 可序列化 | 结构化克隆 | 任意 JS 对象 | -| 浏览器支持 | 所有现代浏览器 | 所有现代浏览器 | 任意环境 | -| 阻塞主线程 | 是 | 否 | 否 | -| 适用场景 | 配置、Token、小数据 | 离线缓存、大数据 | 测试、SSR | +| 特性 | LocalStorageDriver | IndexedDBDriver | MemoryStorageDriver | +| ---------- | ------------------- | ---------------- | ------------------- | +| 持久化 | ✅ | ✅ | ❌ | +| 容量 | 5-10 MB | 数百 MB+ | 受内存限制 | +| 速度 | 快(同步) | 中等(异步 I/O) | 最快 | +| 数据类型 | 仅 JSON 可序列化 | 结构化克隆 | 任意 JS 对象 | +| 浏览器支持 | 所有现代浏览器 | 所有现代浏览器 | 任意环境 | +| 阻塞主线程 | 是 | 否 | 否 | +| 适用场景 | 配置、Token、小数据 | 离线缓存、大数据 | 测试、SSR | --- @@ -388,12 +387,12 @@ class PreferenceManager { constructor() { this.cache = new StorageManager(); - this.state = reactive({...defaultPreferences}); + this.state = reactive({ ...defaultPreferences }); } - initPreferences = async ({namespace}) => { + initPreferences = async ({ namespace }) => { // 用应用命名空间重新初始化 - this.cache = new StorageManager({prefix: namespace}); + this.cache = new StorageManager({ prefix: namespace }); // 从缓存加载偏好设置 const cached = await this.cache.getItem('preferences'); @@ -406,8 +405,7 @@ class PreferenceManager { ## 注意事项 -1. **所有方法都是异步的** — 即使底层是同步的 localStorage,API 也返回 Promise,确保切换 Driver - 时无需改动调用方。 +1. **所有方法都是异步的** — 即使底层是同步的 localStorage,API 也返回 Promise,确保切换 Driver 时无需改动调用方。 2. **TTL 单位是毫秒** — `setItem('key', value, 60000)` 表示 60 秒后过期。 @@ -415,8 +413,6 @@ class PreferenceManager { 4. **前缀隔离是逻辑隔离** — `clear()` 只清除当前前缀下的数据,不影响其他前缀或无前缀的数据。 -5. **错误处理** — LocalStorageDriver 在 JSON 解析失败时自动清除损坏数据; - `PreferenceManager.saveToCache` 内部 try-catch 防止未捕获异常。 +5. **错误处理** — LocalStorageDriver 在 JSON 解析失败时自动清除损坏数据; `PreferenceManager.saveToCache` 内部 try-catch 防止未捕获异常。 -6. **IndexedDB 版本升级** — 如果需要修改 objectStore 结构,需要递增 `dbVersion`。当前实现在 - `upgradeneeded` 事件中自动创建 objectStore。 +6. **IndexedDB 版本升级** — 如果需要修改 objectStore 结构,需要递增 `dbVersion`。当前实现在 `upgradeneeded` 事件中自动创建 objectStore。 diff --git a/packages/@core/base/shared/src/cache/__tests__/storage-manager.test.ts b/packages/@core/base/shared/src/cache/__tests__/storage-manager.test.ts index 6d58cedd2..dfd3fa653 100644 --- a/packages/@core/base/shared/src/cache/__tests__/storage-manager.test.ts +++ b/packages/@core/base/shared/src/cache/__tests__/storage-manager.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -import {MemoryStorageDriver} from '../memory-storage-driver'; +import { MemoryStorageDriver } from '../memory-storage-driver'; import { StorageManager } from '../storage-manager'; describe('storageManager', () => { @@ -15,7 +15,7 @@ describe('storageManager', () => { }); it('should set and get an item', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}); + await storageManager.setItem('user', { age: 30, name: 'John Doe' }); const user = await storageManager.getItem('user'); expect(user).toEqual({ age: 30, name: 'John Doe' }); }); @@ -29,22 +29,22 @@ describe('storageManager', () => { }); it('should remove an item', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}); + await storageManager.setItem('user', { age: 30, name: 'John Doe' }); await storageManager.removeItem('user'); const user = await storageManager.getItem('user'); expect(user).toBeNull(); }); it('should clear all items with the prefix', async () => { - await storageManager.setItem('user1', {age: 30, name: 'John Doe'}); - await storageManager.setItem('user2', {age: 25, name: 'Jane Doe'}); + await storageManager.setItem('user1', { age: 30, name: 'John Doe' }); + await storageManager.setItem('user2', { age: 25, name: 'Jane Doe' }); await storageManager.clear(); expect(await storageManager.getItem('user1')).toBeNull(); expect(await storageManager.getItem('user2')).toBeNull(); }); it('should clear expired items', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}, 1000); // 1秒过期 + await storageManager.setItem('user', { age: 30, name: 'John Doe' }, 1000); // 1秒过期 vi.advanceTimersByTime(1001); // 快进时间 await storageManager.clearExpiredItems(); const user = await storageManager.getItem('user'); @@ -52,7 +52,7 @@ describe('storageManager', () => { }); it('should not clear non-expired items', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}, 10_000); // 10秒过期 + await storageManager.setItem('user', { age: 30, name: 'John Doe' }, 10_000); // 10秒过期 vi.advanceTimersByTime(5000); // 快进时间 await storageManager.clearExpiredItems(); const user = await storageManager.getItem('user'); @@ -65,36 +65,36 @@ describe('storageManager', () => { }); it('should overwrite existing items', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}); - await storageManager.setItem('user', {age: 25, name: 'Jane Doe'}); + await storageManager.setItem('user', { age: 30, name: 'John Doe' }); + await storageManager.setItem('user', { age: 25, name: 'Jane Doe' }); const user = await storageManager.getItem('user'); expect(user).toEqual({ age: 25, name: 'Jane Doe' }); }); it('should handle items without expiry correctly', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}); + await storageManager.setItem('user', { age: 30, name: 'John Doe' }); vi.advanceTimersByTime(5000); const user = await storageManager.getItem('user'); expect(user).toEqual({ age: 30, name: 'John Doe' }); }); it('should remove expired items when accessed', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}, 1000); // 1秒过期 + await storageManager.setItem('user', { age: 30, name: 'John Doe' }, 1000); // 1秒过期 vi.advanceTimersByTime(1001); // 快进时间 const user = await storageManager.getItem('user'); expect(user).toBeNull(); }); it('should not remove non-expired items when accessed', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}, 10_000); // 10秒过期 + await storageManager.setItem('user', { age: 30, name: 'John Doe' }, 10_000); // 10秒过期 vi.advanceTimersByTime(5000); // 快进时间 const user = await storageManager.getItem('user'); expect(user).toEqual({ age: 30, name: 'John Doe' }); }); it('should handle multiple items with different expiry times', async () => { - await storageManager.setItem('user1', {age: 30, name: 'John Doe'}, 1000); // 1秒过期 - await storageManager.setItem('user2', {age: 25, name: 'Jane Doe'}, 2000); // 2秒过期 + await storageManager.setItem('user1', { age: 30, name: 'John Doe' }, 1000); // 1秒过期 + await storageManager.setItem('user2', { age: 25, name: 'Jane Doe' }, 2000); // 2秒过期 vi.advanceTimersByTime(1500); // 快进时间 await storageManager.clearExpiredItems(); const user1 = await storageManager.getItem('user1'); @@ -104,7 +104,7 @@ describe('storageManager', () => { }); it('should handle items with no expiry', async () => { - await storageManager.setItem('user', {age: 30, name: 'John Doe'}); + await storageManager.setItem('user', { age: 30, name: 'John Doe' }); vi.advanceTimersByTime(10_000); // 快进时间 await storageManager.clearExpiredItems(); const user = await storageManager.getItem('user'); @@ -112,8 +112,8 @@ describe('storageManager', () => { }); it('should clear all items correctly', async () => { - await storageManager.setItem('user1', {age: 30, name: 'John Doe'}); - await storageManager.setItem('user2', {age: 25, name: 'Jane Doe'}); + await storageManager.setItem('user1', { age: 30, name: 'John Doe' }); + await storageManager.setItem('user2', { age: 25, name: 'Jane Doe' }); await storageManager.clear(); const user1 = await storageManager.getItem('user1'); const user2 = await storageManager.getItem('user2'); diff --git a/packages/@core/base/shared/src/cache/indexeddb-driver.ts b/packages/@core/base/shared/src/cache/indexeddb-driver.ts index 9875d888a..2b6cdcbc2 100644 --- a/packages/@core/base/shared/src/cache/indexeddb-driver.ts +++ b/packages/@core/base/shared/src/cache/indexeddb-driver.ts @@ -1,4 +1,4 @@ -import type {IStorageDriver} from './types'; +import type { IStorageDriver } from './types'; interface IndexedDBDriverOptions { /** 数据库名称 */ @@ -20,10 +20,10 @@ class IndexedDBDriver implements IStorageDriver { private storeName: string; constructor({ - dbName = 'vben-storage', - dbVersion = 1, - storeName = 'kv-store', - }: IndexedDBDriverOptions = {}) { + dbName = 'vben-storage', + dbVersion = 1, + storeName = 'kv-store', + }: IndexedDBDriverOptions = {}) { this.dbName = dbName; this.dbVersion = dbVersion; this.storeName = storeName; @@ -87,7 +87,7 @@ class IndexedDBDriver implements IStorageDriver { }); } - async setItem(key: string, value: T): Promise { + async setItem(key: string, value: unknown): Promise { const db = await this.getDB(); return new Promise((resolve, reject) => { const tx = db.transaction(this.storeName, 'readwrite'); @@ -133,5 +133,5 @@ class IndexedDBDriver implements IStorageDriver { } } -export {IndexedDBDriver}; -export type {IndexedDBDriverOptions}; +export { IndexedDBDriver }; +export type { IndexedDBDriverOptions }; diff --git a/packages/@core/base/shared/src/cache/local-storage-driver.ts b/packages/@core/base/shared/src/cache/local-storage-driver.ts index 6026246ea..041068f67 100644 --- a/packages/@core/base/shared/src/cache/local-storage-driver.ts +++ b/packages/@core/base/shared/src/cache/local-storage-driver.ts @@ -1,4 +1,4 @@ -import type {IStorageDriver} from './types'; +import type { IStorageDriver } from './types'; type StorageType = 'localStorage' | 'sessionStorage'; @@ -15,8 +15,8 @@ class LocalStorageDriver implements IStorageDriver { private storage: Storage; constructor({ - storageType = 'localStorage', - }: LocalStorageDriverOptions = {}) { + storageType = 'localStorage', + }: LocalStorageDriverOptions = {}) { if (typeof window === 'undefined') { // eslint-disable-next-line unicorn/prefer-type-error -- not a type check, it's an environment check throw new Error( @@ -62,10 +62,10 @@ class LocalStorageDriver implements IStorageDriver { this.storage.removeItem(key); } - async setItem(key: string, value: T): Promise { + async setItem(key: string, value: unknown): Promise { this.storage.setItem(key, JSON.stringify(value)); } } -export {LocalStorageDriver}; -export type {LocalStorageDriverOptions}; +export { LocalStorageDriver }; +export type { LocalStorageDriverOptions }; diff --git a/packages/@core/base/shared/src/cache/memory-storage-driver.ts b/packages/@core/base/shared/src/cache/memory-storage-driver.ts index 530c19ef5..be38d237c 100644 --- a/packages/@core/base/shared/src/cache/memory-storage-driver.ts +++ b/packages/@core/base/shared/src/cache/memory-storage-driver.ts @@ -1,4 +1,4 @@ -import type {IStorageDriver} from './types'; +import type { IStorageDriver } from './types'; /** * 内存存储驱动 @@ -24,9 +24,9 @@ class MemoryStorageDriver implements IStorageDriver { this.store.delete(key); } - async setItem(key: string, value: T): Promise { + async setItem(key: string, value: unknown): Promise { this.store.set(key, value); } } -export {MemoryStorageDriver}; +export { MemoryStorageDriver }; diff --git a/packages/@core/base/shared/src/cache/storage-manager.ts b/packages/@core/base/shared/src/cache/storage-manager.ts index ef42aa7f4..ce0bc3500 100644 --- a/packages/@core/base/shared/src/cache/storage-manager.ts +++ b/packages/@core/base/shared/src/cache/storage-manager.ts @@ -4,8 +4,8 @@ import type { StorageManagerOptions, } from './types'; -import {LocalStorageDriver} from './local-storage-driver'; -import {MemoryStorageDriver} from './memory-storage-driver'; +import { LocalStorageDriver } from './local-storage-driver'; +import { MemoryStorageDriver } from './memory-storage-driver'; /** * 存储管理器(策略模式) @@ -17,7 +17,7 @@ class StorageManager { private driver: IStorageDriver; private prefix: string; - constructor({driver, prefix = ''}: StorageManagerOptions = {}) { + constructor({ driver, prefix = '' }: StorageManagerOptions = {}) { this.driver = driver || this.createDefaultDriver(); this.prefix = prefix; if (!this.prefix && this.driver instanceof LocalStorageDriver) { @@ -106,10 +106,10 @@ class StorageManager { * @param value 值 * @param ttl 存活时间(毫秒) */ - async setItem(key: string, value: T, ttl?: number): Promise { + async setItem(key: string, value: unknown, ttl?: number): Promise { const fullKey = this.getFullKey(key); const expiry = ttl ? Date.now() + ttl : undefined; - const item: StorageItem = { expiry, value }; + const item: StorageItem = { expiry, value }; await this.driver.setItem(fullKey, item); } diff --git a/packages/@core/base/shared/src/cache/types.ts b/packages/@core/base/shared/src/cache/types.ts index c967223a6..fba35b96a 100644 --- a/packages/@core/base/shared/src/cache/types.ts +++ b/packages/@core/base/shared/src/cache/types.ts @@ -17,7 +17,7 @@ interface IStorageDriver { removeItem(key: string): Promise; /** 设置存储项 */ - setItem(key: string, value: T): Promise; + setItem(key: string, value: unknown): Promise; } /** @@ -36,4 +36,4 @@ interface StorageManagerOptions { prefix?: string; } -export type {IStorageDriver, StorageItem, StorageManagerOptions}; +export type { IStorageDriver, StorageItem, StorageManagerOptions }; diff --git a/packages/@core/base/shared/src/utils/__tests__/state-handler.test.ts b/packages/@core/base/shared/src/utils/__tests__/state-handler.test.ts index d11be0681..94844c204 100644 --- a/packages/@core/base/shared/src/utils/__tests__/state-handler.test.ts +++ b/packages/@core/base/shared/src/utils/__tests__/state-handler.test.ts @@ -34,7 +34,9 @@ describe('stateHandler', () => { }, 10); // 等待过程中,期望 Promise 被 reject - await expect(handler.waitForCondition()).rejects.toThrow(); + await expect(handler.waitForCondition()).rejects.toThrow( + 'Condition was set to false', + ); expect(handler.isConditionTrue()).toBe(false); }); diff --git a/packages/@core/base/shared/src/utils/__tests__/util.test.ts b/packages/@core/base/shared/src/utils/__tests__/util.test.ts index dd5846514..ccdeb9692 100644 --- a/packages/@core/base/shared/src/utils/__tests__/util.test.ts +++ b/packages/@core/base/shared/src/utils/__tests__/util.test.ts @@ -138,8 +138,10 @@ describe('getNestedValue', () => { expect(result).toBe(2); }); - it('should return the entire object if path is empty', () => { - expect(() => getNestedValue(data, '')()).toThrow(); + it('should throw if path is empty', () => { + expect(() => getNestedValue(data, '')).toThrow( + 'Path must be a non-empty string', + ); }); it('should handle paths with array indexes', () => { diff --git a/packages/@core/base/shared/src/utils/state-handler.ts b/packages/@core/base/shared/src/utils/state-handler.ts index 438fcda17..a8bbe4f8d 100644 --- a/packages/@core/base/shared/src/utils/state-handler.ts +++ b/packages/@core/base/shared/src/utils/state-handler.ts @@ -1,6 +1,6 @@ export class StateHandler { private condition: boolean = false; - private rejectCondition: (() => void) | null = null; + private rejectCondition: ((reason?: Error) => void) | null = null; private resolveCondition: (() => void) | null = null; isConditionTrue(): boolean { @@ -16,7 +16,7 @@ export class StateHandler { setConditionFalse() { this.condition = false; if (this.rejectCondition) { - this.rejectCondition(); + this.rejectCondition(new Error('Condition was set to false')); this.clearPromises(); } } diff --git a/packages/@core/preferences/src/preferences.ts b/packages/@core/preferences/src/preferences.ts index ce1cd17d4..799f3c1fd 100644 --- a/packages/@core/preferences/src/preferences.ts +++ b/packages/@core/preferences/src/preferences.ts @@ -43,7 +43,7 @@ class PreferenceManager { this.cache = new StorageManager(); // 构造函数不再同步读取缓存,使用默认值初始化 // 真正的缓存加载在 initPreferences 中完成(已经是 async) - this.state = reactive({...defaultPreferences}); + this.state = reactive({ ...defaultPreferences }); this.debouncedSave = useDebounceFn(() => this.saveToCache(), 150); } @@ -180,11 +180,7 @@ class PreferenceManager { * 更新扩展偏好设置 * @param updates - 要更新的扩展偏好设置 */ - updateCustomPreferences = < - TCustomPreferences extends object = CustomPreferencesRecord, - >( - updates: DeepPartial, - ) => { + updateCustomPreferences = (updates: DeepPartial) => { if (!this.customPreferencesExtension) { return; } diff --git a/packages/effects/common-ui/src/components/page/page.vue b/packages/effects/common-ui/src/components/page/page.vue index c2509c838..f5ad4b4e8 100644 --- a/packages/effects/common-ui/src/components/page/page.vue +++ b/packages/effects/common-ui/src/components/page/page.vue @@ -12,8 +12,11 @@ defineOptions({ name: 'Page', }); -const { autoContentHeight = false, heightOffset = 0, footerFixed = false } = - defineProps(); +const { + autoContentHeight = false, + heightOffset = 0, + footerFixed = false, +} = defineProps(); const headerHeight = ref(0); const footerHeight = ref(0); @@ -40,7 +43,7 @@ async function calcContentHeight() { await nextTick(); headerHeight.value = headerRef.value?.offsetHeight || 0; - footerHeight.value = footerFixed ? 0 : (footerRef.value?.offsetHeight || 0); + footerHeight.value = footerFixed ? 0 : footerRef.value?.offsetHeight || 0; setTimeout(() => { shouldAutoHeight.value = true; diff --git a/packages/effects/plugins/src/tiptap/extensions.ts b/packages/effects/plugins/src/tiptap/extensions.ts index 52067ceed..245aa8042 100644 --- a/packages/effects/plugins/src/tiptap/extensions.ts +++ b/packages/effects/plugins/src/tiptap/extensions.ts @@ -66,6 +66,7 @@ function findPlaceholderPos(doc: ProseMirrorNode, blobUrl: string): number { found = offset; return false; } + return true; }); return found; } @@ -272,30 +273,30 @@ function createCustomImage( ...this.parent?.(), uploadImage: () => - ({ editor: cmdEditor }: { editor: CoreEditor }) => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = imageUpload.accept ?? DEFAULT_ACCEPT; - input.style.display = 'none'; + ({ editor: cmdEditor }: { editor: CoreEditor }) => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = imageUpload.accept ?? DEFAULT_ACCEPT; + input.style.display = 'none'; - input.addEventListener('change', () => { - const file = input.files?.[0]; - if (!file) return; + input.addEventListener('change', () => { + const file = input.files?.[0]; + if (!file) return; - const error = validateFile(file, imageUpload); - if (error) { - handleUploadError(new Error(error), imageUpload); - return; - } + const error = validateFile(file, imageUpload); + if (error) { + handleUploadError(new Error(error), imageUpload); + return; + } - createUploadProcess(cmdEditor, file, imageUpload, blobUrlTracker); - input.remove(); - }); + createUploadProcess(cmdEditor, file, imageUpload, blobUrlTracker); + input.remove(); + }); - document.body.append(input); - input.click(); - return true; - }, + document.body.append(input); + input.click(); + return true; + }, }; }, @@ -428,20 +429,20 @@ export function createDefaultTiptapExtensions( }), options.imageUpload ? createCustomImage( - options.imageUpload, - options._blobUrlTracker, - ).configure({ - allowBase64: true, - HTMLAttributes: { - class: 'vben-tiptap__image', - }, - }) + options.imageUpload, + options._blobUrlTracker, + ).configure({ + allowBase64: true, + HTMLAttributes: { + class: 'vben-tiptap__image', + }, + }) : Image.configure({ - allowBase64: true, - HTMLAttributes: { - class: 'vben-tiptap__image', - }, - }), + allowBase64: true, + HTMLAttributes: { + class: 'vben-tiptap__image', + }, + }), Placeholder.configure({ placeholder: options.placeholder ?? $t('ui.tiptap.placeholder'), }), diff --git a/packages/effects/plugins/src/vxe-table/api.ts b/packages/effects/plugins/src/vxe-table/api.ts index 076686ff3..86fe6a129 100644 --- a/packages/effects/plugins/src/vxe-table/api.ts +++ b/packages/effects/plugins/src/vxe-table/api.ts @@ -6,7 +6,7 @@ import type { } from '@vben-core/form-ui'; import type { VxeGridProps } from './types'; -import type {ViewedRowHelper} from './use-viewed-row'; +import type { ViewedRowHelper } from './use-viewed-row'; import { toRaw } from 'vue'; diff --git a/packages/effects/plugins/src/vxe-table/types.ts b/packages/effects/plugins/src/vxe-table/types.ts index ed52eb417..49c21cb95 100644 --- a/packages/effects/plugins/src/vxe-table/types.ts +++ b/packages/effects/plugins/src/vxe-table/types.ts @@ -75,29 +75,29 @@ interface ViewedRowPersistBase { */ export type ViewedRowPersistOptions = | ({ - /** IndexedDB 数据库名称,默认 'viewed-table-db' */ - dbName?: string; - /** IndexedDB 数据库版本,默认 1 */ - dbVersion?: number; - /** 存储 key / prefix(必传) */ - key: string; - /** IndexedDB 对象存储名称,默认 'viewed-table-row' */ - storeName?: string; - type: 'indexedDB'; -} & ViewedRowPersistBase) + /** IndexedDB 数据库名称,默认 'viewed-table-db' */ + dbName?: string; + /** IndexedDB 数据库版本,默认 1 */ + dbVersion?: number; + /** 存储 key / prefix(必传) */ + key: string; + /** IndexedDB 对象存储名称,默认 'viewed-table-row' */ + storeName?: string; + type: 'indexedDB'; + } & ViewedRowPersistBase) | ({ - /** 存储 key(必传) */ - key: string; - type: 'localStorage' | 'sessionStorage'; -} & ViewedRowPersistBase) + /** 存储 key(必传) */ + key: string; + type: 'localStorage' | 'sessionStorage'; + } & ViewedRowPersistBase) | ({ - /** 自定义存储适配器(必传) */ - storage: ViewedRowStorageAdapter; - type: 'custom'; -} & ViewedRowPersistBase) + /** 自定义存储适配器(必传) */ + storage: ViewedRowStorageAdapter; + type: 'custom'; + } & ViewedRowPersistBase) | (ViewedRowPersistBase & { - type: 'memory'; -}); + type: 'memory'; + }); /** * 已查看row设置 diff --git a/packages/effects/plugins/src/vxe-table/use-viewed-row.ts b/packages/effects/plugins/src/vxe-table/use-viewed-row.ts index 365a761ed..741df7cbb 100644 --- a/packages/effects/plugins/src/vxe-table/use-viewed-row.ts +++ b/packages/effects/plugins/src/vxe-table/use-viewed-row.ts @@ -1,4 +1,5 @@ -import type {VxeGridProps as VxeTableGridProps} from 'vxe-table'; +/* eslint-disable unicorn/no-nested-ternary */ +import type { VxeGridProps as VxeTableGridProps } from 'vxe-table'; import type { ViewedRowOptions, @@ -6,9 +7,9 @@ import type { ViewedRowStorageAdapter, } from './types'; -import {isRef, shallowRef, toRaw, triggerRef, watch} from 'vue'; +import { isRef, shallowRef, toRaw, triggerRef, watch } from 'vue'; -import {isBoolean, isFunction} from '@vben/utils'; +import { isBoolean, isFunction } from '@vben/utils'; import { IndexedDBDriver, @@ -16,7 +17,7 @@ import { StorageManager, } from '@vben-core/shared/cache'; -import {useDebounceFn} from '@vueuse/core'; +import { useDebounceFn } from '@vueuse/core'; const DEFAULT_VIEWED_CLASS = 'vxe-row--viewed'; @@ -32,7 +33,7 @@ function createWebStorageAdapter( ttl?: number, ): ViewedRowStorageAdapter { const manager = new StorageManager({ - driver: new LocalStorageDriver({storageType}), + driver: new LocalStorageDriver({ storageType }), }); return { @@ -183,9 +184,9 @@ export function useViewedRow( ) { // ========== 解析持久化配置 ========== const persistOpts: null | ViewedRowPersistOptions = options.persist - ? (typeof options.persist === 'string' - ? {key: options.persist, type: 'localStorage'} - : options.persist) + ? typeof options.persist === 'string' + ? { key: options.persist, type: 'localStorage' } + : options.persist : null; const adapter = createStorageAdapter(options.persist); @@ -341,7 +342,7 @@ export function useViewedRow( function getRowClassName(params: any): string { if (!isViewed(params.row)) return ''; - const {rowClassName} = options; + const { rowClassName } = options; if (rowClassName === undefined || rowClassName === null) { return DEFAULT_VIEWED_CLASS; } @@ -358,7 +359,7 @@ export function useViewedRow( function getRowStyle(params: any): any { if (!isViewed(params.row)) return undefined; - const {rowStyle} = options; + const { rowStyle } = options; if (rowStyle === undefined || rowStyle === null) { return undefined; } @@ -415,11 +416,11 @@ function wrapColumnsForViewedRow( return columns.map((column) => { if (!column || typeof column !== 'object') return column; - const nextColumn = {...column}; + const nextColumn = { ...column }; if (nextColumn.cellRender?.name === 'CellOperation') { - const cellRender = {...nextColumn.cellRender}; - const attrs = {...cellRender.attrs}; + const cellRender = { ...nextColumn.cellRender }; + const attrs = { ...cellRender.attrs }; const originalOnClick = attrs.onClick; attrs.onClick = (params: { code: string; row: any }) => { @@ -515,15 +516,15 @@ export function applyViewedRowOptions( if (!viewedStyle && !originalStyle) return undefined; if (!originalStyle) return viewedStyle; if (!viewedStyle) return originalStyle; - return {...originalStyle, ...viewedStyle}; + return { ...originalStyle, ...viewedStyle }; }; // 拦截 CellOperation columns const actionCodes = !isBoolean(viewedRowConfig) && viewedRowConfig.actionCodes - ? (Array.isArray(viewedRowConfig.actionCodes) + ? Array.isArray(viewedRowConfig.actionCodes) ? viewedRowConfig.actionCodes - : [viewedRowConfig.actionCodes]) + : [viewedRowConfig.actionCodes] : []; if (actionCodes.length > 0 && Array.isArray(mergedOptions.columns)) { diff --git a/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue b/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue index bde99943d..e8ef00975 100644 --- a/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue +++ b/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue @@ -44,7 +44,7 @@ import { VxeGrid, VxeUI } from 'vxe-table'; import { extendProxyOptions } from './extends'; import { useTableForm } from './init'; -import {applyViewedRowOptions, useViewedRow} from './use-viewed-row'; +import { applyViewedRowOptions, useViewedRow } from './use-viewed-row'; import 'vxe-table/styles/cssvar.scss'; import 'vxe-pc-ui/styles/cssvar.scss'; @@ -93,10 +93,10 @@ watch( if (!cfg) return; const keyField = (gridOptions.value?.rowConfig as any)?.keyField || 'id'; - const resolved = isBoolean(cfg) ? {keyField} : {keyField, ...cfg}; + const resolved = isBoolean(cfg) ? { keyField } : { keyField, ...cfg }; gridApi.viewedRowHelper = useViewedRow(resolved); }, - {immediate: true}, + { immediate: true }, ); const { isMobile } = usePreferences(); diff --git a/packages/effects/request/src/request-client/request-client.test.ts b/packages/effects/request/src/request-client/request-client.test.ts index 2d94525e2..661753496 100644 --- a/packages/effects/request/src/request-client/request-client.test.ts +++ b/packages/effects/request/src/request-client/request-client.test.ts @@ -50,24 +50,18 @@ describe('requestClient', () => { it('should handle network errors', async () => { mock.onGet('/test/error').networkError(); - try { - await requestClient.get('/test/error'); - expect(true).toBe(false); - } catch (error: any) { - expect(error.isAxiosError).toBe(true); - expect(error.message).toBe('Network Error'); - } + await expect(requestClient.get('/test/error')).rejects.toMatchObject({ + isAxiosError: true, + message: 'Network Error', + }); }); it('should handle timeout', async () => { mock.onGet('/test/timeout').timeout(); - try { - await requestClient.get('/test/timeout'); - expect(true).toBe(false); - } catch (error: any) { - expect(error.isAxiosError).toBe(true); - expect(error.code).toBe('ECONNABORTED'); - } + await expect(requestClient.get('/test/timeout')).rejects.toMatchObject({ + isAxiosError: true, + code: 'ECONNABORTED', + }); }); it('should successfully upload a file', async () => { @@ -92,7 +86,7 @@ describe('requestClient', () => { mock.onGet('/test/download').reply(200, mockFileContent); - const res = await requestClient.download('/test/download'); + const res = await requestClient.download('/test/download'); expect(res.data).toBeInstanceOf(Blob); }); diff --git a/playground/src/views/demos/features/preferences-extension/index.vue b/playground/src/views/demos/features/preferences-extension/index.vue index 1b7359e44..70822a097 100644 --- a/playground/src/views/demos/features/preferences-extension/index.vue +++ b/playground/src/views/demos/features/preferences-extension/index.vue @@ -105,7 +105,7 @@ function applyPreset(type: 'compact' | 'focus' | 'review') { }, }; - updateCustomPreferences(presetMap[type]); + updateCustomPreferences(presetMap[type]); } function getPriorityColor(priority: DemoTaskItem['priority']) { @@ -136,7 +136,7 @@ function getPriorityColor(priority: DemoTaskItem['priority']) { :title="$t('demos.preferencesExtensionDemo.currentConfig')" > -