This commit is contained in:
xingyu4j
2026-02-11 11:01:44 +08:00
170 changed files with 7055 additions and 2142 deletions

View File

@@ -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": {

View File

@@ -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;

View File

@@ -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": {

View File

@@ -31,6 +31,7 @@ export {
FoldHorizontal,
Fullscreen,
Github,
Grid,
Grip,
GripVertical,
History,
@@ -39,6 +40,7 @@ export {
Info,
InspectionPanel,
Languages,
LayoutGrid,
LoaderCircle,
LockKeyhole,
LogOut,

View File

@@ -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": {

View File

@@ -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';

View 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]);
});
});

View File

@@ -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';

View 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);

View File

@@ -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": {

View File

@@ -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;
/**
* 任意类型的异步函数