feat: new interface pendant can be configured to display hidden

This commit is contained in:
vince
2024-07-10 21:20:11 +08:00
parent db76325d68
commit a765d3bbc0
36 changed files with 256 additions and 612 deletions

View File

@@ -1,132 +0,0 @@
import { describe, expect, it } from 'vitest';
import { flattenObject } from './flatten-object';
describe('flattenObject', () => {
it('should flatten a nested object correctly', () => {
const nestedObject = {
language: 'en',
notifications: {
email: true,
push: {
sound: true,
vibration: false,
},
},
theme: 'light',
};
const expected = {
language: 'en',
notificationsEmail: true,
notificationsPushSound: true,
notificationsPushVibration: false,
theme: 'light',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle empty objects', () => {
const nestedObject = {};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with primitive values', () => {
const nestedObject = {
active: true,
age: 30,
name: 'Alice',
};
const expected = {
active: true,
age: 30,
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with null values', () => {
const nestedObject = {
user: {
age: null,
name: null,
},
};
const expected = {
userAge: null,
userName: null,
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle nested empty objects', () => {
const nestedObject = {
a: {},
b: { c: {} },
};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle arrays within objects', () => {
const nestedObject = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const expected = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should flatten objects with nested arrays correctly', () => {
const nestedObject = {
person: {
hobbies: ['reading', 'gaming'],
name: 'Alice',
},
};
const expected = {
personHobbies: ['reading', 'gaming'],
personName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with undefined values', () => {
const nestedObject = {
user: {
age: undefined,
name: 'Alice',
},
};
const expected = {
userAge: undefined,
userName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
});

View File

@@ -1,82 +0,0 @@
import type { Flatten } from '@vben-core/typings';
import { capitalizeFirstLetter } from '@vben-core/toolkit';
/**
* 将嵌套对象扁平化
* @param obj - 需要扁平化的对象
* @param parentKey - 父键名,用于递归时拼接键名
* @param result - 存储结果的对象
* @returns 扁平化后的对象
*
* 示例:
* const nestedObj = {
* user: {
* name: 'Alice',
* address: {
* city: 'Wonderland',
* zip: '12345'
* }
* },
* items: [
* { id: 1, name: 'Item 1' },
* { id: 2, name: 'Item 2' }
* ],
* active: true
* };
* const flatObj = flattenObject(nestedObj);
* console.log(flatObj);
* 输出:
* {
* userName: 'Alice',
* userAddressCity: 'Wonderland',
* userAddressZip: '12345',
* items: [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' } ],
* active: true
* }
*/
function flattenObject<T extends Record<string, any>>(
obj: T,
parentKey: string = '',
result: Record<string, any> = {},
): Flatten<T> {
Object.keys(obj).forEach((key) => {
const newKey = parentKey
? `${parentKey}${capitalizeFirstLetter(key)}`
: key;
const value = obj[key];
if (value && typeof value === 'object' && !Array.isArray(value)) {
flattenObject(value, newKey, result);
} else {
result[newKey] = value;
}
});
return result as Flatten<T>;
}
export { flattenObject };
// 定义递归类型,用于推断扁平化后的对象类型
// 限制递归深度的辅助类型
// type FlattenDepth<
// T,
// Depth extends number,
// CurrentDepth extends number[] = [],
// > = {
// [K in keyof T as CurrentDepth['length'] extends Depth
// ? K
// : T[K] extends object
// ? `${CurrentDepth['length'] extends 0 ? UnCapitalize<K & string> : Capitalize<K & string>}${keyof FlattenDepth<T[K], Depth, [...CurrentDepth, 1]> extends string ? Capitalize<keyof FlattenDepth<T[K], Depth, [...CurrentDepth, 1]>> : ''}`
// : `${CurrentDepth['length'] extends 0 ? UnCapitalize<K & string> : Capitalize<K & string>}`]: CurrentDepth['length'] extends Depth
// ? T[K]
// : T[K] extends object
// ? FlattenDepth<T[K], Depth, [...CurrentDepth, 1]>[keyof FlattenDepth<
// T[K],
// Depth,
// [...CurrentDepth, 1]
// >]
// : T[K];
// };
// type Flatten<T, Depth extends number = 4> = FlattenDepth<T, Depth>;

View File

@@ -1,4 +1,2 @@
export * from './find-menu-by-path';
export * from './flatten-object';
export * from './merge-route-modules';
export * from './nested-object';

View File

@@ -1,115 +0,0 @@
import { describe, expect, it } from 'vitest';
import { nestedObject } from './nested-object';
describe('nestedObject', () => {
it('should convert flat object to nested object with level 1', () => {
const flatObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
const expectedNestedObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 2', () => {
const flatObject = {
appAnotherKeyExample: 2,
appCommonName: 1,
appSomeOtherKey: 3,
};
const expectedNestedObject = {
app: {
anotherKeyExample: 2,
commonName: 1,
someOtherKey: 3,
},
};
expect(nestedObject(flatObject, 2)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 3', () => {
const flatObject = {
appAnotherKeyExampleValue: 2,
appCommonNameKey: 1,
appSomeOtherKeyItem: 3,
};
const expectedNestedObject = {
app: {
another: {
keyExampleValue: 2,
},
common: {
nameKey: 1,
},
some: {
otherKeyItem: 3,
},
},
};
expect(nestedObject(flatObject, 3)).toEqual(expectedNestedObject);
});
it('should handle empty object', () => {
const flatObject = {};
const expectedNestedObject = {};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle single key object', () => {
const flatObject = {
singleKey: 1,
};
const expectedNestedObject = {
singleKey: 1,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle complex keys', () => {
const flatObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
const expectedNestedObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should correctly nest an object based on the specified level', () => {
const obj = {
oneFiveSix: 'Value156',
oneTwoFour: 'Value124',
oneTwoThree: 'Value123',
};
const nested = nestedObject(obj, 2);
expect(nested).toEqual({
one: {
fiveSix: 'Value156',
twoFour: 'Value124',
twoThree: 'Value123',
},
});
});
});

View File

@@ -1,70 +0,0 @@
import { toLowerCaseFirstLetter } from '@vben-core/toolkit';
/**
* 将扁平对象转换为嵌套对象。
*
* @template T - 输入对象值的类型
* @param {Record<string, T>} obj - 要转换的扁平对象
* @param {number} level - 嵌套的层级
* @returns {T} 嵌套对象
*
* @example
* 将扁平对象转换为嵌套对象,嵌套层级为 1
* const flatObject = {
* 'commonAppName': 1,
* 'anotherKeyExample': 2,
* 'someOtherKey': 3
* };
* const nestedObject = nestedObject(flatObject, 1);
* console.log(nestedObject);
* 输出:
* {
* commonAppName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
*
* @example
* 将扁平对象转换为嵌套对象,嵌套层级为 2
* const flatObject = {
* 'appCommonName': 1,
* 'appAnotherKeyExample': 2,
* 'appSomeOtherKey': 3
* };
* const nestedObject = nestedObject(flatObject, 2);
* console.log(nestedObject);
* 输出:
* {
* app: {
* commonName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
* }
*/
function nestedObject<T>(obj: Record<string, T>, level: number): T {
const result: any = {};
for (const key in obj) {
const keys = key.split(/(?=[A-Z])/);
// 将驼峰式分割为数组;
let current = result;
for (let i = 0; i < keys.length; i++) {
const lowerKey = keys[i].toLowerCase();
if (i === level - 1) {
const remainingKeys = keys.slice(i).join(''); // 保留后续部分作为键的一部分
current[toLowerCaseFirstLetter(remainingKeys)] = obj[key];
break;
} else {
current[lowerKey] = current[lowerKey] || {};
current = current[lowerKey];
}
}
}
return result as T;
}
export { nestedObject };

View File

@@ -17,7 +17,6 @@ const defaultPreferences: Preferences = {
layout: 'sidebar-nav',
locale: 'zh-CN',
name: 'Vben Admin Pro',
semiDarkMenu: true,
},
breadcrumb: {
enable: true,
@@ -82,6 +81,7 @@ const defaultPreferences: Preferences = {
colorWarning: 'hsl(42 84% 61%)',
mode: 'dark',
radius: '0.5',
semiDarkMenu: true,
},
transition: {
enable: true,
@@ -89,6 +89,15 @@ const defaultPreferences: Preferences = {
name: 'fade-slide',
progress: true,
},
widget: {
aiAssistant: true,
fullscreen: true,
globalSearch: true,
languageToggle: true,
notification: true,
sidebarToggle: true,
themeToggle: true,
},
};
export { defaultPreferences };

View File

@@ -1,3 +1,5 @@
import type { DeepPartial } from '@vben-core/typings';
import type { Preferences } from './types';
import { preferencesManager } from './preferences';
@@ -5,10 +7,6 @@ import { preferencesManager } from './preferences';
// 偏好设置(带有层级关系)
const preferences: Preferences = preferencesManager.getPreferences();
// 扁平化后的偏好设置
// const flatPreferences: Flatten<Preferences> =
// preferencesManager.getFlatPreferences();
// 更新偏好设置
const updatePreferences =
preferencesManager.updatePreferences.bind(preferencesManager);
@@ -20,9 +18,13 @@ const resetPreferences =
const clearPreferencesCache =
preferencesManager.clearCache.bind(preferencesManager);
function defineOverridesPreferences(preferences: DeepPartial<Preferences>) {
return preferences;
}
export {
clearPreferencesCache,
// flatPreferences,
defineOverridesPreferences,
preferences,
preferencesManager,
resetPreferences,

View File

@@ -46,8 +46,6 @@ interface AppPreferences {
locale: SupportedLanguagesType;
/** 应用名 */
name: string;
/** 是否开启半深色菜单只在theme='light'时生效) */
semiDarkMenu: boolean;
}
interface BreadcrumbPreferences {
@@ -164,6 +162,8 @@ interface ThemePreferences {
mode: ThemeModeType;
/** 圆角 */
radius: string;
/** 是否开启半深色菜单只在theme='light'时生效) */
semiDarkMenu: boolean;
}
interface TransitionPreferences {
@@ -177,6 +177,23 @@ interface TransitionPreferences {
progress: boolean;
}
interface WidgetPreferences {
/** 是否开启vben助手部件 */
aiAssistant: boolean;
/** 是否启用全屏部件 */
fullscreen: boolean;
/** 是否启用全局搜索部件 */
globalSearch: boolean;
/** 是否启用语言切换部件 */
languageToggle: boolean;
/** 是否显示通知部件 */
notification: boolean;
/** 是否显示侧边栏显示/隐藏部件 */
sidebarToggle: boolean;
/** 是否显示主题切换部件 */
themeToggle: boolean;
}
interface Preferences {
/** 全局配置 */
app: AppPreferences;
@@ -202,6 +219,8 @@ interface Preferences {
theme: ThemePreferences;
/** 动画配置 */
transition: TransitionPreferences;
/** 功能配置 */
widget: WidgetPreferences;
}
type PreferencesKeys = keyof Preferences;
@@ -230,4 +249,5 @@ export type {
ThemeModeType,
ThemePreferences,
TransitionPreferences,
WidgetPreferences,
};