feat: add dashboard page

This commit is contained in:
vben
2024-06-23 23:18:55 +08:00
parent 199d5506ac
commit c58c0797ba
100 changed files with 1908 additions and 1081 deletions

View File

@@ -22,6 +22,6 @@
},
"dependencies": {
"@iconify/vue": "^4.1.2",
"vue": "^3.4.30"
"vue": "^3.4.31"
}
}

View File

@@ -4,6 +4,7 @@ import { Icon } from '@iconify/vue';
function createIcon(name: string) {
return defineComponent({
name: `SvgIcon-${name}`,
setup(props, { attrs }) {
return () => h(Icon, { icon: name, ...props, ...attrs });
},

View File

@@ -36,7 +36,7 @@
}
},
"dependencies": {
"@vue/shared": "^3.4.30",
"@vue/shared": "^3.4.31",
"clsx": "2.1.1",
"defu": "^6.1.4",
"nprogress": "^0.2.0",

View File

@@ -0,0 +1,140 @@
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
import { getElementVisibleHeight } from './dom'; // 假设函数位于 utils.ts 中
describe('getElementVisibleHeight', () => {
// Mocking the getBoundingClientRect method
const mockGetBoundingClientRect = vi.fn();
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
beforeAll(() => {
// Mock getBoundingClientRect method
Element.prototype.getBoundingClientRect = mockGetBoundingClientRect;
});
afterAll(() => {
// Restore original getBoundingClientRect method
Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
});
it('should return 0 if the element is null or undefined', () => {
expect(getElementVisibleHeight(null)).toBe(0);
expect(getElementVisibleHeight()).toBe(0);
});
it('should return the visible height of the element', () => {
// Mock the getBoundingClientRect return value
mockGetBoundingClientRect.mockReturnValue({
bottom: 500,
height: 400,
left: 0,
right: 0,
toJSON: () => ({}),
top: 100,
width: 0,
x: 0,
y: 0,
});
const mockElement = document.createElement('div');
document.body.append(mockElement);
// Mocking window.innerHeight and document.documentElement.clientHeight
const originalInnerHeight = window.innerHeight;
const originalClientHeight = document.documentElement.clientHeight;
Object.defineProperty(window, 'innerHeight', {
value: 600,
writable: true,
});
Object.defineProperty(document.documentElement, 'clientHeight', {
value: 600,
writable: true,
});
expect(getElementVisibleHeight(mockElement)).toBe(400);
// Restore original values
Object.defineProperty(window, 'innerHeight', {
value: originalInnerHeight,
writable: true,
});
Object.defineProperty(document.documentElement, 'clientHeight', {
value: originalClientHeight,
writable: true,
});
mockElement.remove();
});
it('should return the visible height when element is partially out of viewport', () => {
// Mock the getBoundingClientRect return value
mockGetBoundingClientRect.mockReturnValue({
bottom: 300,
height: 400,
left: 0,
right: 0,
toJSON: () => ({}),
top: -100,
width: 0,
x: 0,
y: 0,
});
const mockElement = document.createElement('div');
document.body.append(mockElement);
// Mocking window.innerHeight and document.documentElement.clientHeight
const originalInnerHeight = window.innerHeight;
const originalClientHeight = document.documentElement.clientHeight;
Object.defineProperty(window, 'innerHeight', {
value: 600,
writable: true,
});
Object.defineProperty(document.documentElement, 'clientHeight', {
value: 600,
writable: true,
});
expect(getElementVisibleHeight(mockElement)).toBe(300);
// Restore original values
Object.defineProperty(window, 'innerHeight', {
value: originalInnerHeight,
writable: true,
});
Object.defineProperty(document.documentElement, 'clientHeight', {
value: originalClientHeight,
writable: true,
});
mockElement.remove();
});
it('should return 0 if the element is completely out of viewport', () => {
// Mock the getBoundingClientRect return value
mockGetBoundingClientRect.mockReturnValue({
bottom: -100,
height: 400,
left: 0,
right: 0,
toJSON: () => ({}),
top: -500,
width: 0,
x: 0,
y: 0,
});
const mockElement = document.createElement('div');
document.body.append(mockElement);
expect(getElementVisibleHeight(mockElement)).toBe(0);
mockElement.remove();
});
});

View File

@@ -0,0 +1,24 @@
/**
* 获取元素可见高度
* @param element
* @returns
*/
function getElementVisibleHeight(
element?: HTMLElement | null | undefined,
): number {
if (!element) {
return 0;
}
const rect = element.getBoundingClientRect();
const viewHeight = Math.max(
document.documentElement.clientHeight,
window.innerHeight,
);
const top = Math.max(rect.top, 0);
const bottom = Math.min(rect.bottom, viewHeight);
return Math.max(0, bottom - top);
}
export { getElementVisibleHeight };

View File

@@ -1,5 +1,6 @@
export * from './cn';
export * from './diff';
export * from './dom';
export * from './hash';
export * from './inference';
export * from './letter';
@@ -7,5 +8,6 @@ export * from './merge';
export * from './namespace';
export * from './nprogress';
export * from './tree';
export * from './unique';
export * from './update-css-variables';
export * from './window';

View File

@@ -97,11 +97,20 @@ function isWindowsOs(): boolean {
return windowsRegex.test(navigator.userAgent);
}
/**
* 检查传入的值是否为数字
* @param value
*/
function isNumber(value: any): value is number {
return typeof value === 'number' && Number.isFinite(value);
}
export {
isEmpty,
isFunction,
isHttpUrl,
isMacOs,
isNumber,
isObject,
isString,
isUndefined,

View File

@@ -0,0 +1,61 @@
import { describe, expect, it } from 'vitest';
import { uniqueByField } from './unique';
describe('uniqueByField', () => {
it('should return an array with unique items based on id field', () => {
const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
{ id: 1, name: 'Duplicate Item' },
];
const uniqueItems = uniqueByField(items, 'id');
// Assert expected results
expect(uniqueItems).toHaveLength(3); // After deduplication, there should be three objects left
expect(uniqueItems).toEqual([
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]);
});
it('should return an empty array when input array is empty', () => {
const items: any[] = []; // Empty array
const uniqueItems = uniqueByField(items, 'id');
// Assert expected results
expect(uniqueItems).toEqual([]);
});
it('should handle arrays with only one item correctly', () => {
const items = [{ id: 1, name: 'Item 1' }];
const uniqueItems = uniqueByField(items, 'id');
// Assert expected results
expect(uniqueItems).toHaveLength(1);
expect(uniqueItems).toEqual([{ id: 1, name: 'Item 1' }]);
});
it('should preserve the order of the first occurrence of each item', () => {
const items = [
{ id: 2, name: 'Item 2' },
{ id: 1, name: 'Item 1' },
{ id: 3, name: 'Item 3' },
{ id: 1, name: 'Duplicate Item' },
];
const uniqueItems = uniqueByField(items, 'id');
// Assert expected results (order of first occurrences preserved)
expect(uniqueItems).toEqual([
{ id: 2, name: 'Item 2' },
{ id: 1, name: 'Item 1' },
{ id: 3, name: 'Item 3' },
]);
});
});

View File

@@ -0,0 +1,15 @@
/**
* 根据指定字段对对象数组进行去重
* @param arr 要去重的对象数组
* @param key 去重依据的字段名
* @returns 去重后的对象数组
*/
function uniqueByField<T>(arr: T[], key: keyof T): T[] {
const seen = new Map<any, T>();
return arr.filter((item) => {
const value = item[key];
return seen.has(value) ? false : (seen.set(value, item), true);
});
}
export { uniqueByField };

View File

@@ -39,7 +39,7 @@
}
},
"dependencies": {
"vue": "^3.4.30",
"vue": "^3.4.31",
"vue-router": "^4.4.0"
}
}