style: apply vsh lint formatting (#7923)
parent
84e77f64ea
commit
d71c81e8ff
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
"@vben/layouts": patch
|
'@vben/layouts': patch
|
||||||
---
|
---
|
||||||
|
|
||||||
fix: update primary color when toggling dark/light mode with custom theme
|
fix: update primary color when toggling dark/light mode with custom theme
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
"@vben/common-ui": patch
|
'@vben/common-ui': patch
|
||||||
---
|
---
|
||||||
|
|
||||||
fix: skip fixed footer height in auto-content-height calculation
|
fix: skip fixed footer height in auto-content-height calculation
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
"@vben/icons": patch
|
'@vben/icons': patch
|
||||||
---
|
---
|
||||||
|
|
||||||
fix: guard svg icon loading during docs SSR
|
fix: guard svg icon loading during docs SSR
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
"@vben-core/shadcn-ui": patch
|
'@vben-core/shadcn-ui': patch
|
||||||
---
|
---
|
||||||
|
|
||||||
fix: preserve tree default value when treeData starts empty
|
fix: preserve tree default value when treeData starts empty
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,9 @@ const content = ref('<p>开始编辑你的内容...</p>');
|
||||||
<VbenTiptap v-model="content" />
|
<VbenTiptap v-model="content" />
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<p class="text-sm text-gray-500">当前内容:</p>
|
<p class="text-sm text-gray-500">当前内容:</p>
|
||||||
<pre
|
<pre class="mt-2 p-2 bg-gray-100 rounded text-xs overflow-auto max-h-40">
|
||||||
class="mt-2 p-2 bg-gray-100 rounded text-xs overflow-auto max-h-40"
|
{{ content }}
|
||||||
>{{ content }}</pre
|
</pre>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# Cache 模块
|
# Cache 模块
|
||||||
|
|
||||||
基于**策略模式**的异步存储管理方案,支持多种存储后端(localStorage、IndexedDB、Memory),提供统一的 API
|
基于**策略模式**的异步存储管理方案,支持多种存储后端(localStorage、IndexedDB、Memory),提供统一的 API 接口。
|
||||||
接口。
|
|
||||||
|
|
||||||
## 架构设计
|
## 架构设计
|
||||||
|
|
||||||
|
|
@ -22,11 +21,11 @@
|
||||||
|
|
||||||
**分层职责:**
|
**分层职责:**
|
||||||
|
|
||||||
| 层级 | 职责 |
|
| 层级 | 职责 |
|
||||||
|------------------|----------------------------|
|
| ---------------- | -------------------------------------------- |
|
||||||
| `StorageManager` | 命名空间前缀隔离、TTL 过期检查、统一对外 API |
|
| `StorageManager` | 命名空间前缀隔离、TTL 过期检查、统一对外 API |
|
||||||
| `IStorageDriver` | 纯粹的 KV 存取抽象接口 |
|
| `IStorageDriver` | 纯粹的 KV 存取抽象接口 |
|
||||||
| 各 Driver 实现 | 对接具体存储引擎,不感知前缀和 TTL |
|
| 各 Driver 实现 | 对接具体存储引擎,不感知前缀和 TTL |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -35,9 +34,9 @@
|
||||||
### 基本使用(默认 localStorage)
|
### 基本使用(默认 localStorage)
|
||||||
|
|
||||||
```typescript
|
```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
|
// 使用 IndexedDB
|
||||||
//new StorageManager({ driver: new IndexedDBDriver(), prefix: 'app' });
|
//new StorageManager({ driver: new IndexedDBDriver(), prefix: 'app' });
|
||||||
|
|
||||||
|
|
@ -48,14 +47,14 @@ const cache = new StorageManager({prefix: 'myapp'});
|
||||||
//new StorageManager({ driver: new MemoryStorageDriver(), prefix: 'test' });
|
//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');
|
const user = await cache.getItem('user');
|
||||||
// => { name: '张三', age: 28 }
|
// => { name: '张三', age: 28 }
|
||||||
|
|
||||||
// 带默认值读取
|
// 带默认值读取
|
||||||
const settings = await cache.getItem('settings', {theme: 'light'});
|
const settings = await cache.getItem('settings', { theme: 'light' });
|
||||||
// 如果不存在,返回 { theme: 'light' }
|
// 如果不存在,返回 { theme: 'light' }
|
||||||
|
|
||||||
// 删除数据
|
// 删除数据
|
||||||
|
|
@ -68,7 +67,7 @@ await cache.clear();
|
||||||
### 带 TTL 过期
|
### 带 TTL 过期
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const cache = new StorageManager({prefix: 'session'});
|
const cache = new StorageManager({ prefix: 'session' });
|
||||||
|
|
||||||
// 设置 5 分钟后过期(TTL 单位为毫秒)
|
// 设置 5 分钟后过期(TTL 单位为毫秒)
|
||||||
await cache.setItem('token', 'abc123', 5 * 60 * 1000);
|
await cache.setItem('token', 'abc123', 5 * 60 * 1000);
|
||||||
|
|
@ -191,20 +190,20 @@ const cache = new StorageManager({
|
||||||
new StorageManager(options?: StorageManagerOptions)
|
new StorageManager(options?: StorageManagerOptions)
|
||||||
```
|
```
|
||||||
|
|
||||||
| 参数 | 类型 | 默认值 | 说明 |
|
| 参数 | 类型 | 默认值 | 说明 |
|
||||||
|----------|------------------|----------------------------|--------------|
|
| --- | --- | --- | --- |
|
||||||
| `driver` | `IStorageDriver` | `new LocalStorageDriver()` | 存储驱动实例 |
|
| `driver` | `IStorageDriver` | `new LocalStorageDriver()` | 存储驱动实例 |
|
||||||
| `prefix` | `string` | `''` | 键前缀,用于命名空间隔离 |
|
| `prefix` | `string` | `''` | 键前缀,用于命名空间隔离 |
|
||||||
|
|
||||||
#### 方法
|
#### 方法
|
||||||
|
|
||||||
| 方法 | 签名 | 说明 |
|
| 方法 | 签名 | 说明 |
|
||||||
|---------------------|-------------------------------------------------------------------------|-------------------|
|
| --- | --- | --- |
|
||||||
| `getItem` | `getItem<T>(key: string, defaultValue?: T \| null): Promise<T \| null>` | 获取存储项,过期或不存在返回默认值 |
|
| `getItem` | `getItem<T>(key: string, defaultValue?: T \| null): Promise<T \| null>` | 获取存储项,过期或不存在返回默认值 |
|
||||||
| `setItem` | `setItem<T>(key: string, value: T, ttl?: number): Promise<void>` | 设置存储项,可选 TTL(毫秒) |
|
| `setItem` | `setItem<T>(key: string, value: T, ttl?: number): Promise<void>` | 设置存储项,可选 TTL(毫秒) |
|
||||||
| `removeItem` | `removeItem(key: string): Promise<void>` | 删除指定存储项 |
|
| `removeItem` | `removeItem(key: string): Promise<void>` | 删除指定存储项 |
|
||||||
| `clear` | `clear(): Promise<void>` | 清除当前前缀下所有存储项 |
|
| `clear` | `clear(): Promise<void>` | 清除当前前缀下所有存储项 |
|
||||||
| `clearExpiredItems` | `clearExpiredItems(): Promise<void>` | 主动清理所有过期项 |
|
| `clearExpiredItems` | `clearExpiredItems(): Promise<void>` | 主动清理所有过期项 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -233,7 +232,7 @@ interface IStorageDriver {
|
||||||
### 自定义 Driver
|
### 自定义 Driver
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import type {IStorageDriver} from '@vben-core/shared/cache';
|
import type { IStorageDriver } from '@vben-core/shared/cache';
|
||||||
|
|
||||||
class CookieStorageDriver implements IStorageDriver {
|
class CookieStorageDriver implements IStorageDriver {
|
||||||
async getItem<T>(key: string): Promise<null | T> {
|
async getItem<T>(key: string): Promise<null | T> {
|
||||||
|
|
@ -301,11 +300,11 @@ function createStorageManager(prefix: string) {
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 不同模块使用不同前缀,互不干扰
|
// 不同模块使用不同前缀,互不干扰
|
||||||
const userCache = new StorageManager({prefix: 'user'});
|
const userCache = new StorageManager({ prefix: 'user' });
|
||||||
const configCache = new StorageManager({prefix: 'config'});
|
const configCache = new StorageManager({ prefix: 'config' });
|
||||||
|
|
||||||
await userCache.setItem('profile', {name: '张三'});
|
await userCache.setItem('profile', { name: '张三' });
|
||||||
await configCache.setItem('profile', {theme: 'dark'});
|
await configCache.setItem('profile', { theme: 'dark' });
|
||||||
|
|
||||||
// 各自独立
|
// 各自独立
|
||||||
await userCache.getItem('profile'); // => { name: '张三' }
|
await userCache.getItem('profile'); // => { name: '张三' }
|
||||||
|
|
@ -356,24 +355,24 @@ interface StorageItem<T> {
|
||||||
|
|
||||||
采用**惰性删除 + 主动清理**双重策略:
|
采用**惰性删除 + 主动清理**双重策略:
|
||||||
|
|
||||||
| 策略 | 触发时机 | 说明 |
|
| 策略 | 触发时机 | 说明 |
|
||||||
|------|--------------------------|---------------------|
|
| --- | --- | --- |
|
||||||
| 惰性删除 | 调用 `getItem` 时 | 读取时检查过期,过期则删除并返回默认值 |
|
| 惰性删除 | 调用 `getItem` 时 | 读取时检查过期,过期则删除并返回默认值 |
|
||||||
| 主动清理 | 调用 `clearExpiredItems` 时 | 遍历所有带前缀的 key,删除已过期项 |
|
| 主动清理 | 调用 `clearExpiredItems` 时 | 遍历所有带前缀的 key,删除已过期项 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 各 Driver 对比
|
## 各 Driver 对比
|
||||||
|
|
||||||
| 特性 | LocalStorageDriver | IndexedDBDriver | MemoryStorageDriver |
|
| 特性 | LocalStorageDriver | IndexedDBDriver | MemoryStorageDriver |
|
||||||
|-------|--------------------|-----------------|---------------------|
|
| ---------- | ------------------- | ---------------- | ------------------- |
|
||||||
| 持久化 | ✅ | ✅ | ❌ |
|
| 持久化 | ✅ | ✅ | ❌ |
|
||||||
| 容量 | 5-10 MB | 数百 MB+ | 受内存限制 |
|
| 容量 | 5-10 MB | 数百 MB+ | 受内存限制 |
|
||||||
| 速度 | 快(同步) | 中等(异步 I/O) | 最快 |
|
| 速度 | 快(同步) | 中等(异步 I/O) | 最快 |
|
||||||
| 数据类型 | 仅 JSON 可序列化 | 结构化克隆 | 任意 JS 对象 |
|
| 数据类型 | 仅 JSON 可序列化 | 结构化克隆 | 任意 JS 对象 |
|
||||||
| 浏览器支持 | 所有现代浏览器 | 所有现代浏览器 | 任意环境 |
|
| 浏览器支持 | 所有现代浏览器 | 所有现代浏览器 | 任意环境 |
|
||||||
| 阻塞主线程 | 是 | 否 | 否 |
|
| 阻塞主线程 | 是 | 否 | 否 |
|
||||||
| 适用场景 | 配置、Token、小数据 | 离线缓存、大数据 | 测试、SSR |
|
| 适用场景 | 配置、Token、小数据 | 离线缓存、大数据 | 测试、SSR |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -388,12 +387,12 @@ class PreferenceManager {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.cache = new StorageManager();
|
this.cache = new StorageManager();
|
||||||
this.state = reactive<Preferences>({...defaultPreferences});
|
this.state = reactive<Preferences>({ ...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>('preferences');
|
const cached = await this.cache.getItem<Preferences>('preferences');
|
||||||
|
|
@ -406,8 +405,7 @@ class PreferenceManager {
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **所有方法都是异步的** — 即使底层是同步的 localStorage,API 也返回 Promise,确保切换 Driver
|
1. **所有方法都是异步的** — 即使底层是同步的 localStorage,API 也返回 Promise,确保切换 Driver 时无需改动调用方。
|
||||||
时无需改动调用方。
|
|
||||||
|
|
||||||
2. **TTL 单位是毫秒** — `setItem('key', value, 60000)` 表示 60 秒后过期。
|
2. **TTL 单位是毫秒** — `setItem('key', value, 60000)` 表示 60 秒后过期。
|
||||||
|
|
||||||
|
|
@ -415,8 +413,6 @@ class PreferenceManager {
|
||||||
|
|
||||||
4. **前缀隔离是逻辑隔离** — `clear()` 只清除当前前缀下的数据,不影响其他前缀或无前缀的数据。
|
4. **前缀隔离是逻辑隔离** — `clear()` 只清除当前前缀下的数据,不影响其他前缀或无前缀的数据。
|
||||||
|
|
||||||
5. **错误处理** — LocalStorageDriver 在 JSON 解析失败时自动清除损坏数据;
|
5. **错误处理** — LocalStorageDriver 在 JSON 解析失败时自动清除损坏数据; `PreferenceManager.saveToCache` 内部 try-catch 防止未捕获异常。
|
||||||
`PreferenceManager.saveToCache` 内部 try-catch 防止未捕获异常。
|
|
||||||
|
|
||||||
6. **IndexedDB 版本升级** — 如果需要修改 objectStore 结构,需要递增 `dbVersion`。当前实现在
|
6. **IndexedDB 版本升级** — 如果需要修改 objectStore 结构,需要递增 `dbVersion`。当前实现在 `upgradeneeded` 事件中自动创建 objectStore。
|
||||||
`upgradeneeded` 事件中自动创建 objectStore。
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
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';
|
import { StorageManager } from '../storage-manager';
|
||||||
|
|
||||||
describe('storageManager', () => {
|
describe('storageManager', () => {
|
||||||
|
|
@ -15,7 +15,7 @@ describe('storageManager', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set and get an item', async () => {
|
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');
|
const user = await storageManager.getItem('user');
|
||||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||||
});
|
});
|
||||||
|
|
@ -29,22 +29,22 @@ describe('storageManager', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove an item', async () => {
|
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');
|
await storageManager.removeItem('user');
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
expect(user).toBeNull();
|
expect(user).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear all items with the prefix', async () => {
|
it('should clear all items with the prefix', async () => {
|
||||||
await storageManager.setItem('user1', {age: 30, name: 'John Doe'});
|
await storageManager.setItem('user1', { age: 30, name: 'John Doe' });
|
||||||
await storageManager.setItem('user2', {age: 25, name: 'Jane Doe'});
|
await storageManager.setItem('user2', { age: 25, name: 'Jane Doe' });
|
||||||
await storageManager.clear();
|
await storageManager.clear();
|
||||||
expect(await storageManager.getItem('user1')).toBeNull();
|
expect(await storageManager.getItem('user1')).toBeNull();
|
||||||
expect(await storageManager.getItem('user2')).toBeNull();
|
expect(await storageManager.getItem('user2')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear expired items', async () => {
|
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); // 快进时间
|
vi.advanceTimersByTime(1001); // 快进时间
|
||||||
await storageManager.clearExpiredItems();
|
await storageManager.clearExpiredItems();
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
|
|
@ -52,7 +52,7 @@ describe('storageManager', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not clear non-expired items', async () => {
|
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); // 快进时间
|
vi.advanceTimersByTime(5000); // 快进时间
|
||||||
await storageManager.clearExpiredItems();
|
await storageManager.clearExpiredItems();
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
|
|
@ -65,36 +65,36 @@ describe('storageManager', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should overwrite existing items', async () => {
|
it('should overwrite existing items', async () => {
|
||||||
await storageManager.setItem('user', {age: 30, name: 'John Doe'});
|
await storageManager.setItem('user', { age: 30, name: 'John Doe' });
|
||||||
await storageManager.setItem('user', {age: 25, name: 'Jane Doe'});
|
await storageManager.setItem('user', { age: 25, name: 'Jane Doe' });
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
expect(user).toEqual({ age: 25, name: 'Jane Doe' });
|
expect(user).toEqual({ age: 25, name: 'Jane Doe' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle items without expiry correctly', async () => {
|
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);
|
vi.advanceTimersByTime(5000);
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove expired items when accessed', async () => {
|
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); // 快进时间
|
vi.advanceTimersByTime(1001); // 快进时间
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
expect(user).toBeNull();
|
expect(user).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not remove non-expired items when accessed', async () => {
|
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); // 快进时间
|
vi.advanceTimersByTime(5000); // 快进时间
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
expect(user).toEqual({ age: 30, name: 'John Doe' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle multiple items with different expiry times', async () => {
|
it('should handle multiple items with different expiry times', async () => {
|
||||||
await storageManager.setItem('user1', {age: 30, name: 'John Doe'}, 1000); // 1秒过期
|
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('user2', { age: 25, name: 'Jane Doe' }, 2000); // 2秒过期
|
||||||
vi.advanceTimersByTime(1500); // 快进时间
|
vi.advanceTimersByTime(1500); // 快进时间
|
||||||
await storageManager.clearExpiredItems();
|
await storageManager.clearExpiredItems();
|
||||||
const user1 = await storageManager.getItem('user1');
|
const user1 = await storageManager.getItem('user1');
|
||||||
|
|
@ -104,7 +104,7 @@ describe('storageManager', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle items with no expiry', async () => {
|
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); // 快进时间
|
vi.advanceTimersByTime(10_000); // 快进时间
|
||||||
await storageManager.clearExpiredItems();
|
await storageManager.clearExpiredItems();
|
||||||
const user = await storageManager.getItem('user');
|
const user = await storageManager.getItem('user');
|
||||||
|
|
@ -112,8 +112,8 @@ describe('storageManager', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear all items correctly', async () => {
|
it('should clear all items correctly', async () => {
|
||||||
await storageManager.setItem('user1', {age: 30, name: 'John Doe'});
|
await storageManager.setItem('user1', { age: 30, name: 'John Doe' });
|
||||||
await storageManager.setItem('user2', {age: 25, name: 'Jane Doe'});
|
await storageManager.setItem('user2', { age: 25, name: 'Jane Doe' });
|
||||||
await storageManager.clear();
|
await storageManager.clear();
|
||||||
const user1 = await storageManager.getItem('user1');
|
const user1 = await storageManager.getItem('user1');
|
||||||
const user2 = await storageManager.getItem('user2');
|
const user2 = await storageManager.getItem('user2');
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type {IStorageDriver} from './types';
|
import type { IStorageDriver } from './types';
|
||||||
|
|
||||||
interface IndexedDBDriverOptions {
|
interface IndexedDBDriverOptions {
|
||||||
/** 数据库名称 */
|
/** 数据库名称 */
|
||||||
|
|
@ -20,10 +20,10 @@ class IndexedDBDriver implements IStorageDriver {
|
||||||
private storeName: string;
|
private storeName: string;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
dbName = 'vben-storage',
|
dbName = 'vben-storage',
|
||||||
dbVersion = 1,
|
dbVersion = 1,
|
||||||
storeName = 'kv-store',
|
storeName = 'kv-store',
|
||||||
}: IndexedDBDriverOptions = {}) {
|
}: IndexedDBDriverOptions = {}) {
|
||||||
this.dbName = dbName;
|
this.dbName = dbName;
|
||||||
this.dbVersion = dbVersion;
|
this.dbVersion = dbVersion;
|
||||||
this.storeName = storeName;
|
this.storeName = storeName;
|
||||||
|
|
@ -133,5 +133,5 @@ class IndexedDBDriver implements IStorageDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {IndexedDBDriver};
|
export { IndexedDBDriver };
|
||||||
export type {IndexedDBDriverOptions};
|
export type { IndexedDBDriverOptions };
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type {IStorageDriver} from './types';
|
import type { IStorageDriver } from './types';
|
||||||
|
|
||||||
type StorageType = 'localStorage' | 'sessionStorage';
|
type StorageType = 'localStorage' | 'sessionStorage';
|
||||||
|
|
||||||
|
|
@ -15,8 +15,8 @@ class LocalStorageDriver implements IStorageDriver {
|
||||||
private storage: Storage;
|
private storage: Storage;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
storageType = 'localStorage',
|
storageType = 'localStorage',
|
||||||
}: LocalStorageDriverOptions = {}) {
|
}: LocalStorageDriverOptions = {}) {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
// eslint-disable-next-line unicorn/prefer-type-error -- not a type check, it's an environment check
|
// eslint-disable-next-line unicorn/prefer-type-error -- not a type check, it's an environment check
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -67,5 +67,5 @@ class LocalStorageDriver implements IStorageDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {LocalStorageDriver};
|
export { LocalStorageDriver };
|
||||||
export type {LocalStorageDriverOptions};
|
export type { LocalStorageDriverOptions };
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type {IStorageDriver} from './types';
|
import type { IStorageDriver } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内存存储驱动
|
* 内存存储驱动
|
||||||
|
|
@ -29,4 +29,4 @@ class MemoryStorageDriver implements IStorageDriver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {MemoryStorageDriver};
|
export { MemoryStorageDriver };
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import type {
|
||||||
StorageManagerOptions,
|
StorageManagerOptions,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
import {LocalStorageDriver} from './local-storage-driver';
|
import { LocalStorageDriver } from './local-storage-driver';
|
||||||
import {MemoryStorageDriver} from './memory-storage-driver';
|
import { MemoryStorageDriver } from './memory-storage-driver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 存储管理器(策略模式)
|
* 存储管理器(策略模式)
|
||||||
|
|
@ -17,7 +17,7 @@ class StorageManager {
|
||||||
private driver: IStorageDriver;
|
private driver: IStorageDriver;
|
||||||
private prefix: string;
|
private prefix: string;
|
||||||
|
|
||||||
constructor({driver, prefix = ''}: StorageManagerOptions = {}) {
|
constructor({ driver, prefix = '' }: StorageManagerOptions = {}) {
|
||||||
this.driver = driver || this.createDefaultDriver();
|
this.driver = driver || this.createDefaultDriver();
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
if (!this.prefix && this.driver instanceof LocalStorageDriver) {
|
if (!this.prefix && this.driver instanceof LocalStorageDriver) {
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,4 @@ interface StorageManagerOptions {
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type {IStorageDriver, StorageItem, StorageManagerOptions};
|
export type { IStorageDriver, StorageItem, StorageManagerOptions };
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class PreferenceManager {
|
||||||
this.cache = new StorageManager();
|
this.cache = new StorageManager();
|
||||||
// 构造函数不再同步读取缓存,使用默认值初始化
|
// 构造函数不再同步读取缓存,使用默认值初始化
|
||||||
// 真正的缓存加载在 initPreferences 中完成(已经是 async)
|
// 真正的缓存加载在 initPreferences 中完成(已经是 async)
|
||||||
this.state = reactive<Preferences>({...defaultPreferences});
|
this.state = reactive<Preferences>({ ...defaultPreferences });
|
||||||
this.debouncedSave = useDebounceFn(() => this.saveToCache(), 150);
|
this.debouncedSave = useDebounceFn(() => this.saveToCache(), 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,11 @@ defineOptions({
|
||||||
name: 'Page',
|
name: 'Page',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { autoContentHeight = false, heightOffset = 0, footerFixed = false } =
|
const {
|
||||||
defineProps<PageProps>();
|
autoContentHeight = false,
|
||||||
|
heightOffset = 0,
|
||||||
|
footerFixed = false,
|
||||||
|
} = defineProps<PageProps>();
|
||||||
|
|
||||||
const headerHeight = ref(0);
|
const headerHeight = ref(0);
|
||||||
const footerHeight = ref(0);
|
const footerHeight = ref(0);
|
||||||
|
|
@ -40,7 +43,7 @@ async function calcContentHeight() {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
headerHeight.value = headerRef.value?.offsetHeight || 0;
|
headerHeight.value = headerRef.value?.offsetHeight || 0;
|
||||||
|
|
||||||
footerHeight.value = footerFixed ? 0 : (footerRef.value?.offsetHeight || 0);
|
footerHeight.value = footerFixed ? 0 : footerRef.value?.offsetHeight || 0;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
shouldAutoHeight.value = true;
|
shouldAutoHeight.value = true;
|
||||||
|
|
|
||||||
|
|
@ -272,30 +272,30 @@ function createCustomImage(
|
||||||
...this.parent?.(),
|
...this.parent?.(),
|
||||||
uploadImage:
|
uploadImage:
|
||||||
() =>
|
() =>
|
||||||
({ editor: cmdEditor }: { editor: CoreEditor }) => {
|
({ editor: cmdEditor }: { editor: CoreEditor }) => {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.accept = imageUpload.accept ?? DEFAULT_ACCEPT;
|
input.accept = imageUpload.accept ?? DEFAULT_ACCEPT;
|
||||||
input.style.display = 'none';
|
input.style.display = 'none';
|
||||||
|
|
||||||
input.addEventListener('change', () => {
|
input.addEventListener('change', () => {
|
||||||
const file = input.files?.[0];
|
const file = input.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
const error = validateFile(file, imageUpload);
|
const error = validateFile(file, imageUpload);
|
||||||
if (error) {
|
if (error) {
|
||||||
handleUploadError(new Error(error), imageUpload);
|
handleUploadError(new Error(error), imageUpload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
createUploadProcess(cmdEditor, file, imageUpload, blobUrlTracker);
|
createUploadProcess(cmdEditor, file, imageUpload, blobUrlTracker);
|
||||||
input.remove();
|
input.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.append(input);
|
document.body.append(input);
|
||||||
input.click();
|
input.click();
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -428,20 +428,20 @@ export function createDefaultTiptapExtensions(
|
||||||
}),
|
}),
|
||||||
options.imageUpload
|
options.imageUpload
|
||||||
? createCustomImage(
|
? createCustomImage(
|
||||||
options.imageUpload,
|
options.imageUpload,
|
||||||
options._blobUrlTracker,
|
options._blobUrlTracker,
|
||||||
).configure({
|
).configure({
|
||||||
allowBase64: true,
|
allowBase64: true,
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
class: 'vben-tiptap__image',
|
class: 'vben-tiptap__image',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
: Image.configure({
|
: Image.configure({
|
||||||
allowBase64: true,
|
allowBase64: true,
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
class: 'vben-tiptap__image',
|
class: 'vben-tiptap__image',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
Placeholder.configure({
|
Placeholder.configure({
|
||||||
placeholder: options.placeholder ?? $t('ui.tiptap.placeholder'),
|
placeholder: options.placeholder ?? $t('ui.tiptap.placeholder'),
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import type {
|
||||||
} from '@vben-core/form-ui';
|
} from '@vben-core/form-ui';
|
||||||
|
|
||||||
import type { VxeGridProps } from './types';
|
import type { VxeGridProps } from './types';
|
||||||
import type {ViewedRowHelper} from './use-viewed-row';
|
import type { ViewedRowHelper } from './use-viewed-row';
|
||||||
|
|
||||||
import { toRaw } from 'vue';
|
import { toRaw } from 'vue';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,29 +75,29 @@ interface ViewedRowPersistBase {
|
||||||
*/
|
*/
|
||||||
export type ViewedRowPersistOptions =
|
export type ViewedRowPersistOptions =
|
||||||
| ({
|
| ({
|
||||||
/** IndexedDB 数据库名称,默认 'viewed-table-db' */
|
/** IndexedDB 数据库名称,默认 'viewed-table-db' */
|
||||||
dbName?: string;
|
dbName?: string;
|
||||||
/** IndexedDB 数据库版本,默认 1 */
|
/** IndexedDB 数据库版本,默认 1 */
|
||||||
dbVersion?: number;
|
dbVersion?: number;
|
||||||
/** 存储 key / prefix(必传) */
|
/** 存储 key / prefix(必传) */
|
||||||
key: string;
|
key: string;
|
||||||
/** IndexedDB 对象存储名称,默认 'viewed-table-row' */
|
/** IndexedDB 对象存储名称,默认 'viewed-table-row' */
|
||||||
storeName?: string;
|
storeName?: string;
|
||||||
type: 'indexedDB';
|
type: 'indexedDB';
|
||||||
} & ViewedRowPersistBase)
|
} & ViewedRowPersistBase)
|
||||||
| ({
|
| ({
|
||||||
/** 存储 key(必传) */
|
/** 存储 key(必传) */
|
||||||
key: string;
|
key: string;
|
||||||
type: 'localStorage' | 'sessionStorage';
|
type: 'localStorage' | 'sessionStorage';
|
||||||
} & ViewedRowPersistBase)
|
} & ViewedRowPersistBase)
|
||||||
| ({
|
| ({
|
||||||
/** 自定义存储适配器(必传) */
|
/** 自定义存储适配器(必传) */
|
||||||
storage: ViewedRowStorageAdapter;
|
storage: ViewedRowStorageAdapter;
|
||||||
type: 'custom';
|
type: 'custom';
|
||||||
} & ViewedRowPersistBase)
|
} & ViewedRowPersistBase)
|
||||||
| (ViewedRowPersistBase & {
|
| (ViewedRowPersistBase & {
|
||||||
type: 'memory';
|
type: 'memory';
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已查看row设置
|
* 已查看row设置
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type {VxeGridProps as VxeTableGridProps} from 'vxe-table';
|
import type { VxeGridProps as VxeTableGridProps } from 'vxe-table';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ViewedRowOptions,
|
ViewedRowOptions,
|
||||||
|
|
@ -6,9 +6,9 @@ import type {
|
||||||
ViewedRowStorageAdapter,
|
ViewedRowStorageAdapter,
|
||||||
} from './types';
|
} 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 {
|
import {
|
||||||
IndexedDBDriver,
|
IndexedDBDriver,
|
||||||
|
|
@ -16,7 +16,7 @@ import {
|
||||||
StorageManager,
|
StorageManager,
|
||||||
} from '@vben-core/shared/cache';
|
} from '@vben-core/shared/cache';
|
||||||
|
|
||||||
import {useDebounceFn} from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
|
|
||||||
const DEFAULT_VIEWED_CLASS = 'vxe-row--viewed';
|
const DEFAULT_VIEWED_CLASS = 'vxe-row--viewed';
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ function createWebStorageAdapter(
|
||||||
ttl?: number,
|
ttl?: number,
|
||||||
): ViewedRowStorageAdapter {
|
): ViewedRowStorageAdapter {
|
||||||
const manager = new StorageManager({
|
const manager = new StorageManager({
|
||||||
driver: new LocalStorageDriver({storageType}),
|
driver: new LocalStorageDriver({ storageType }),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -182,11 +182,13 @@ export function useViewedRow<T = any>(
|
||||||
options: ViewedRowOptions<T> & { keyField: string },
|
options: ViewedRowOptions<T> & { keyField: string },
|
||||||
) {
|
) {
|
||||||
// ========== 解析持久化配置 ==========
|
// ========== 解析持久化配置 ==========
|
||||||
const persistOpts: null | ViewedRowPersistOptions = options.persist
|
let persistOpts: null | ViewedRowPersistOptions = null;
|
||||||
? (typeof options.persist === 'string'
|
if (options.persist) {
|
||||||
? {key: options.persist, type: 'localStorage'}
|
persistOpts =
|
||||||
: options.persist)
|
typeof options.persist === 'string'
|
||||||
: null;
|
? { key: options.persist, type: 'localStorage' }
|
||||||
|
: options.persist;
|
||||||
|
}
|
||||||
|
|
||||||
const adapter = createStorageAdapter(options.persist);
|
const adapter = createStorageAdapter(options.persist);
|
||||||
const maxSize = persistOpts?.maxSize ?? 100;
|
const maxSize = persistOpts?.maxSize ?? 100;
|
||||||
|
|
@ -341,7 +343,7 @@ export function useViewedRow<T = any>(
|
||||||
function getRowClassName(params: any): string {
|
function getRowClassName(params: any): string {
|
||||||
if (!isViewed(params.row)) return '';
|
if (!isViewed(params.row)) return '';
|
||||||
|
|
||||||
const {rowClassName} = options;
|
const { rowClassName } = options;
|
||||||
if (rowClassName === undefined || rowClassName === null) {
|
if (rowClassName === undefined || rowClassName === null) {
|
||||||
return DEFAULT_VIEWED_CLASS;
|
return DEFAULT_VIEWED_CLASS;
|
||||||
}
|
}
|
||||||
|
|
@ -358,7 +360,7 @@ export function useViewedRow<T = any>(
|
||||||
function getRowStyle(params: any): any {
|
function getRowStyle(params: any): any {
|
||||||
if (!isViewed(params.row)) return undefined;
|
if (!isViewed(params.row)) return undefined;
|
||||||
|
|
||||||
const {rowStyle} = options;
|
const { rowStyle } = options;
|
||||||
if (rowStyle === undefined || rowStyle === null) {
|
if (rowStyle === undefined || rowStyle === null) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -415,11 +417,11 @@ function wrapColumnsForViewedRow(
|
||||||
return columns.map((column) => {
|
return columns.map((column) => {
|
||||||
if (!column || typeof column !== 'object') return column;
|
if (!column || typeof column !== 'object') return column;
|
||||||
|
|
||||||
const nextColumn = {...column};
|
const nextColumn = { ...column };
|
||||||
|
|
||||||
if (nextColumn.cellRender?.name === 'CellOperation') {
|
if (nextColumn.cellRender?.name === 'CellOperation') {
|
||||||
const cellRender = {...nextColumn.cellRender};
|
const cellRender = { ...nextColumn.cellRender };
|
||||||
const attrs = {...cellRender.attrs};
|
const attrs = { ...cellRender.attrs };
|
||||||
const originalOnClick = attrs.onClick;
|
const originalOnClick = attrs.onClick;
|
||||||
|
|
||||||
attrs.onClick = (params: { code: string; row: any }) => {
|
attrs.onClick = (params: { code: string; row: any }) => {
|
||||||
|
|
@ -515,16 +517,16 @@ export function applyViewedRowOptions(
|
||||||
if (!viewedStyle && !originalStyle) return undefined;
|
if (!viewedStyle && !originalStyle) return undefined;
|
||||||
if (!originalStyle) return viewedStyle;
|
if (!originalStyle) return viewedStyle;
|
||||||
if (!viewedStyle) return originalStyle;
|
if (!viewedStyle) return originalStyle;
|
||||||
return {...originalStyle, ...viewedStyle};
|
return { ...originalStyle, ...viewedStyle };
|
||||||
};
|
};
|
||||||
|
|
||||||
// 拦截 CellOperation columns
|
// 拦截 CellOperation columns
|
||||||
const actionCodes =
|
let actionCodes: string[] = [];
|
||||||
!isBoolean(viewedRowConfig) && viewedRowConfig.actionCodes
|
if (!isBoolean(viewedRowConfig) && viewedRowConfig.actionCodes) {
|
||||||
? (Array.isArray(viewedRowConfig.actionCodes)
|
actionCodes = Array.isArray(viewedRowConfig.actionCodes)
|
||||||
? viewedRowConfig.actionCodes
|
? viewedRowConfig.actionCodes
|
||||||
: [viewedRowConfig.actionCodes])
|
: [viewedRowConfig.actionCodes];
|
||||||
: [];
|
}
|
||||||
|
|
||||||
if (actionCodes.length > 0 && Array.isArray(mergedOptions.columns)) {
|
if (actionCodes.length > 0 && Array.isArray(mergedOptions.columns)) {
|
||||||
mergedOptions.columns = wrapColumnsForViewedRow(
|
mergedOptions.columns = wrapColumnsForViewedRow(
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ import { VxeGrid, VxeUI } from 'vxe-table';
|
||||||
|
|
||||||
import { extendProxyOptions } from './extends';
|
import { extendProxyOptions } from './extends';
|
||||||
import { useTableForm } from './init';
|
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-table/styles/cssvar.scss';
|
||||||
import 'vxe-pc-ui/styles/cssvar.scss';
|
import 'vxe-pc-ui/styles/cssvar.scss';
|
||||||
|
|
@ -93,10 +93,10 @@ watch(
|
||||||
if (!cfg) return;
|
if (!cfg) return;
|
||||||
|
|
||||||
const keyField = (gridOptions.value?.rowConfig as any)?.keyField || 'id';
|
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);
|
gridApi.viewedRowHelper = useViewedRow(resolved);
|
||||||
},
|
},
|
||||||
{immediate: true},
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isMobile } = usePreferences();
|
const { isMobile } = usePreferences();
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type {OnActionClickParams, VxeGridProps} from '#/adapter/vxe-table';
|
import type { OnActionClickParams, VxeGridProps } from '#/adapter/vxe-table';
|
||||||
|
|
||||||
import {ref} from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import {Page, useVbenModal} from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
import {$t} from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
import {Button, message} from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
import {useVbenVxeGrid} from '#/adapter/vxe-table';
|
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import {getExampleTableApi} from '#/api';
|
import { getExampleTableApi } from '#/api';
|
||||||
|
|
||||||
interface RowType {
|
interface RowType {
|
||||||
category: string;
|
category: string;
|
||||||
|
|
@ -26,12 +26,12 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||||
labelField: 'category',
|
labelField: 'category',
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{title: '序号', type: 'seq', width: 50},
|
{ title: '序号', type: 'seq', width: 50 },
|
||||||
{field: 'category', sortable: true, title: 'Category'},
|
{ field: 'category', sortable: true, title: 'Category' },
|
||||||
{field: 'color', sortable: true, title: 'Color'},
|
{ field: 'color', sortable: true, title: 'Color' },
|
||||||
{field: 'productName', sortable: true, title: 'Product Name'},
|
{ field: 'productName', sortable: true, title: 'Product Name' },
|
||||||
{field: 'price', sortable: true, title: 'Price'},
|
{ field: 'price', sortable: true, title: 'Price' },
|
||||||
{field: 'releaseDate', formatter: 'formatDateTime', title: 'DateTime'},
|
{ field: 'releaseDate', formatter: 'formatDateTime', title: 'DateTime' },
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
cellRender: {
|
cellRender: {
|
||||||
|
|
@ -61,7 +61,7 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||||
keepSource: true,
|
keepSource: true,
|
||||||
proxyConfig: {
|
proxyConfig: {
|
||||||
ajax: {
|
ajax: {
|
||||||
query: async ({page, sort}) => {
|
query: async ({ page, sort }) => {
|
||||||
return await getExampleTableApi({
|
return await getExampleTableApi({
|
||||||
page: page.currentPage,
|
page: page.currentPage,
|
||||||
pageSize: page.pageSize,
|
pageSize: page.pageSize,
|
||||||
|
|
@ -73,7 +73,7 @@ const gridOptions: VxeGridProps<RowType> = {
|
||||||
sort: true,
|
sort: true,
|
||||||
},
|
},
|
||||||
sortConfig: {
|
sortConfig: {
|
||||||
defaultSort: {field: 'category', order: 'desc'},
|
defaultSort: { field: 'category', order: 'desc' },
|
||||||
remote: true,
|
remote: true,
|
||||||
},
|
},
|
||||||
toolbarConfig: {
|
toolbarConfig: {
|
||||||
|
|
@ -103,7 +103,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function onActionClick({code, row}: OnActionClickParams<RowType>) {
|
function onActionClick({ code, row }: OnActionClickParams<RowType>) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'edit': {
|
case 'edit': {
|
||||||
onEdit(row);
|
onEdit(row);
|
||||||
|
|
@ -123,9 +123,9 @@ const editRow = ref<RowType>();
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
draggable: true,
|
draggable: true,
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
modalApi.setState({loading: true});
|
modalApi.setState({ loading: true });
|
||||||
editRow.value && gridApi.markRowAsViewed(editRow.value);
|
editRow.value && gridApi.markRowAsViewed(editRow.value);
|
||||||
modalApi.setState({loading: false});
|
modalApi.setState({ loading: false });
|
||||||
modalApi.close();
|
modalApi.close();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -149,7 +149,7 @@ function onStyleSet() {
|
||||||
gridApi.setState({
|
gridApi.setState({
|
||||||
viewedRowOptions: {
|
viewedRowOptions: {
|
||||||
rowStyle: () => {
|
rowStyle: () => {
|
||||||
return isStyle.value ? {backgroundColor: 'gray'} : '';
|
return isStyle.value ? { backgroundColor: 'gray' } : '';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue