Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/design",
|
||||
"version": "5.5.9",
|
||||
"version": "5.6.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
html {
|
||||
@apply text-foreground bg-background font-sans;
|
||||
|
||||
scroll-behavior: smooth;
|
||||
font-size: var(--font-size-base, 16px);
|
||||
font-variation-settings: normal;
|
||||
font-synthesis-weight: none;
|
||||
line-height: 1.15;
|
||||
text-size-adjust: 100%;
|
||||
scroll-behavior: smooth;
|
||||
text-rendering: optimizelegibility;
|
||||
text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
/* -webkit-font-smoothing: antialiased;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/icons",
|
||||
"version": "5.5.9",
|
||||
"version": "5.6.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
||||
@@ -31,6 +31,7 @@ export {
|
||||
FoldHorizontal,
|
||||
Fullscreen,
|
||||
Github,
|
||||
Grid,
|
||||
Grip,
|
||||
GripVertical,
|
||||
History,
|
||||
@@ -39,6 +40,7 @@ export {
|
||||
Info,
|
||||
InspectionPanel,
|
||||
Languages,
|
||||
LayoutGrid,
|
||||
LoaderCircle,
|
||||
LockKeyhole,
|
||||
LogOut,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/shared",
|
||||
"version": "5.5.9",
|
||||
"version": "5.6.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
||||
@@ -22,6 +22,8 @@ export const VBEN_LOGO_URL =
|
||||
*/
|
||||
export const VBEN_PREVIEW_URL = 'https://www.vben.pro';
|
||||
|
||||
export const VBEN_ANTDV_NEXT_PREVIEW_URL = 'https://antdv-next.vben.pro';
|
||||
|
||||
export const VBEN_ELE_PREVIEW_URL = 'https://ele.vben.pro';
|
||||
|
||||
export const VBEN_NAIVE_PREVIEW_URL = 'https://naive.vben.pro';
|
||||
|
||||
107
packages/@core/base/shared/src/utils/__tests__/stack.test.ts
Normal file
107
packages/@core/base/shared/src/utils/__tests__/stack.test.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { createStack, Stack } from '../stack';
|
||||
|
||||
describe('stack', () => {
|
||||
let stack: Stack<number>;
|
||||
|
||||
beforeEach(() => {
|
||||
stack = new Stack<number>();
|
||||
});
|
||||
|
||||
it('push & size should work', () => {
|
||||
stack.push(1, 2);
|
||||
|
||||
expect(stack.size).toBe(2);
|
||||
});
|
||||
|
||||
it('peek should return top element without removing it', () => {
|
||||
stack.push(1, 2);
|
||||
|
||||
expect(stack.peek()).toBe(2);
|
||||
expect(stack.size).toBe(2);
|
||||
});
|
||||
|
||||
it('pop should remove and return top element', () => {
|
||||
stack.push(1, 2);
|
||||
|
||||
expect(stack.pop()).toBe(2);
|
||||
expect(stack.size).toBe(1);
|
||||
expect(stack.peek()).toBe(1);
|
||||
});
|
||||
|
||||
it('pop on empty stack should return undefined', () => {
|
||||
expect(stack.pop()).toBeUndefined();
|
||||
expect(stack.peek()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clear should remove all elements', () => {
|
||||
stack.push(1, 2);
|
||||
|
||||
stack.clear();
|
||||
|
||||
expect(stack.size).toBe(0);
|
||||
expect(stack.peek()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('toArray should return a shallow copy', () => {
|
||||
stack.push(1, 2);
|
||||
|
||||
const arr = stack.toArray();
|
||||
arr.push(3);
|
||||
|
||||
expect(stack.size).toBe(2);
|
||||
expect(stack.toArray()).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('dedup should remove existing item before push', () => {
|
||||
stack.push(1, 2, 1);
|
||||
|
||||
expect(stack.toArray()).toEqual([2, 1]);
|
||||
expect(stack.size).toBe(2);
|
||||
});
|
||||
|
||||
it('dedup = false should allow duplicate items', () => {
|
||||
const s = new Stack<number>(false);
|
||||
|
||||
s.push(1, 1, 1);
|
||||
|
||||
expect(s.toArray()).toEqual([1, 1, 1]);
|
||||
expect(s.size).toBe(3);
|
||||
});
|
||||
|
||||
it('remove should delete all matching items', () => {
|
||||
stack.push(1, 2, 1);
|
||||
|
||||
stack.remove(1);
|
||||
|
||||
expect(stack.toArray()).toEqual([2]);
|
||||
expect(stack.size).toBe(1);
|
||||
});
|
||||
|
||||
it('maxSize should limit stack capacity', () => {
|
||||
const s = new Stack<number>(true, 3);
|
||||
|
||||
s.push(1, 2, 3, 4);
|
||||
|
||||
expect(s.toArray()).toEqual([2, 3, 4]);
|
||||
expect(s.size).toBe(3);
|
||||
});
|
||||
|
||||
it('dedup + maxSize should work together', () => {
|
||||
const s = new Stack<number>(true, 3);
|
||||
|
||||
s.push(1, 2, 3, 2); // 去重并重新入栈
|
||||
|
||||
expect(s.toArray()).toEqual([1, 3, 2]);
|
||||
expect(s.size).toBe(3);
|
||||
});
|
||||
|
||||
it('createStack should create a stack instance', () => {
|
||||
const s = createStack<number>(true, 2);
|
||||
|
||||
s.push(1, 2, 3);
|
||||
|
||||
expect(s.toArray()).toEqual([2, 3]);
|
||||
});
|
||||
});
|
||||
@@ -10,6 +10,7 @@ export * from './letter';
|
||||
export * from './merge';
|
||||
export * from './nprogress';
|
||||
export * from './resources';
|
||||
export * from './stack';
|
||||
export * from './state-handler';
|
||||
export * from './time';
|
||||
export * from './to';
|
||||
|
||||
103
packages/@core/base/shared/src/utils/stack.ts
Normal file
103
packages/@core/base/shared/src/utils/stack.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @zh_CN 栈数据结构
|
||||
*/
|
||||
export class Stack<T> {
|
||||
/**
|
||||
* @zh_CN 栈内元素数量
|
||||
*/
|
||||
get size() {
|
||||
return this.items.length;
|
||||
}
|
||||
/**
|
||||
* @zh_CN 是否去重
|
||||
*/
|
||||
private readonly dedup: boolean;
|
||||
/**
|
||||
* @zh_CN 栈内元素
|
||||
*/
|
||||
private items: T[] = [];
|
||||
|
||||
/**
|
||||
* @zh_CN 栈的最大容量
|
||||
*/
|
||||
private readonly maxSize?: number;
|
||||
|
||||
constructor(dedup = true, maxSize?: number) {
|
||||
this.maxSize = maxSize;
|
||||
this.dedup = dedup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 清空栈内元素
|
||||
*/
|
||||
clear() {
|
||||
this.items.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 查看栈顶元素
|
||||
* @returns 栈顶元素
|
||||
*/
|
||||
peek(): T | undefined {
|
||||
return this.items[this.items.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 出栈
|
||||
* @returns 栈顶元素
|
||||
*/
|
||||
pop(): T | undefined {
|
||||
return this.items.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 入栈
|
||||
* @param items 要入栈的元素
|
||||
*/
|
||||
push(...items: T[]) {
|
||||
items.forEach((item) => {
|
||||
// 去重
|
||||
if (this.dedup) {
|
||||
const index = this.items.indexOf(item);
|
||||
if (index !== -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
}
|
||||
this.items.push(item);
|
||||
if (this.maxSize && this.items.length > this.maxSize) {
|
||||
this.items.splice(0, this.items.length - this.maxSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @zh_CN 移除栈内元素
|
||||
* @param itemList 要移除的元素列表
|
||||
*/
|
||||
remove(...itemList: T[]) {
|
||||
this.items = this.items.filter((i) => !itemList.includes(i));
|
||||
}
|
||||
/**
|
||||
* @zh_CN 保留栈内元素
|
||||
* @param itemList 要保留的元素列表
|
||||
*/
|
||||
retain(itemList: T[]) {
|
||||
this.items = this.items.filter((i) => itemList.includes(i));
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 转换为数组
|
||||
* @returns 栈内元素数组
|
||||
*/
|
||||
toArray(): T[] {
|
||||
return [...this.items];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 创建一个栈实例
|
||||
* @param dedup 是否去重
|
||||
* @param maxSize 栈的最大容量
|
||||
* @returns 栈实例
|
||||
*/
|
||||
export const createStack = <T>(dedup = true, maxSize?: number) =>
|
||||
new Stack<T>(dedup, maxSize);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/typings",
|
||||
"version": "5.5.9",
|
||||
"version": "5.6.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
||||
34
packages/@core/base/typings/src/helper.d.ts
vendored
34
packages/@core/base/typings/src/helper.d.ts
vendored
@@ -1,20 +1,38 @@
|
||||
import type { ComputedRef, MaybeRef } from 'vue';
|
||||
|
||||
/**
|
||||
* 类型级递归中增加深度计数
|
||||
*/
|
||||
type Increment<A extends unknown[]> = [...A, unknown];
|
||||
/**
|
||||
* 深层递归所有属性为可选
|
||||
*/
|
||||
type DeepPartial<T> = T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
type DeepPartial<
|
||||
T,
|
||||
D extends number = 10,
|
||||
C extends unknown[] = [],
|
||||
> = C['length'] extends D
|
||||
? T
|
||||
: T extends object
|
||||
? {
|
||||
[P in keyof T]?: DeepPartial<T[P], D, Increment<C>>;
|
||||
}
|
||||
: T;
|
||||
|
||||
/**
|
||||
* 深层递归所有属性为只读
|
||||
*/
|
||||
type DeepReadonly<T> = {
|
||||
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
|
||||
};
|
||||
type DeepReadonly<
|
||||
T,
|
||||
D extends number = 10,
|
||||
C extends unknown[] = [],
|
||||
> = C['length'] extends D
|
||||
? T
|
||||
: T extends object
|
||||
? {
|
||||
readonly [P in keyof T]: DeepReadonly<T[P], D, Increment<C>>;
|
||||
}
|
||||
: T;
|
||||
|
||||
/**
|
||||
* 任意类型的异步函数
|
||||
|
||||
Reference in New Issue
Block a user