feat: new interface pendant can be configured to display hidden
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
@@ -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>;
|
@@ -1,4 +1,2 @@
|
||||
export * from './find-menu-by-path';
|
||||
export * from './flatten-object';
|
||||
export * from './merge-route-modules';
|
||||
export * from './nested-object';
|
||||
|
@@ -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',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
@@ -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 };
|
@@ -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 };
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
};
|
||||
|
Reference in New Issue
Block a user