befly-shared 1.3.9 → 1.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{types/arrayToTree.ts → dist/types/arrayToTree.d.ts} +0 -2
- package/dist/types/arrayToTree.js +4 -0
- package/{types/deepTransformKeys.ts → dist/types/deepTransformKeys.d.ts} +0 -4
- package/dist/types/deepTransformKeys.js +6 -0
- package/{types/genShortId.ts → dist/types/genShortId.d.ts} +0 -1
- package/dist/types/genShortId.js +4 -0
- package/{types/hashPassword.ts → dist/types/hashPassword.d.ts} +0 -1
- package/dist/types/hashPassword.js +4 -0
- package/{types/normalizePathnameListInput.ts → dist/types/normalizePathnameListInput.d.ts} +0 -1
- package/dist/types/normalizePathnameListInput.js +4 -0
- package/{types/scanViewsDir.ts → dist/types/scanViewsDir.d.ts} +0 -7
- package/dist/types/scanViewsDir.js +4 -0
- package/{types/withDefaultColumns.ts → dist/types/withDefaultColumns.d.ts} +0 -1
- package/dist/types/withDefaultColumns.js +4 -0
- package/dist/utils/arrayToTree.d.ts +14 -0
- package/{utils/arrayToTree.ts → dist/utils/arrayToTree.js} +23 -54
- package/dist/utils/deepTransformKeys.d.ts +84 -0
- package/{utils/deepTransformKeys.ts → dist/utils/deepTransformKeys.js} +20 -53
- package/dist/utils/fieldClear.d.ts +11 -0
- package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +12 -42
- package/dist/utils/genShortId.d.ts +10 -0
- package/{utils/genShortId.ts → dist/utils/genShortId.js} +1 -1
- package/dist/utils/hashPassword.d.ts +11 -0
- package/{utils/hashPassword.ts → dist/utils/hashPassword.js} +1 -6
- package/dist/utils/normalizePathnameListInput.d.ts +16 -0
- package/{utils/normalizePathnameListInput.ts → dist/utils/normalizePathnameListInput.js} +4 -14
- package/dist/utils/scanViewsDir.d.ts +43 -0
- package/{utils/scanViewsDir.ts → dist/utils/scanViewsDir.js} +18 -52
- package/dist/utils/withDefaultColumns.d.ts +14 -0
- package/{utils/withDefaultColumns.ts → dist/utils/withDefaultColumns.js} +7 -17
- package/package.json +16 -8
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* arrayToTree 类型定义(类型模块,仅供 type 引用)。
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
export type ArrayToTreeResult<T extends Record<string, any>> = {
|
|
6
5
|
flat: Array<T>;
|
|
7
6
|
tree: Array<T>;
|
|
8
7
|
map: Map<string, T>;
|
|
9
8
|
};
|
|
10
|
-
|
|
11
9
|
export type ArrayToTree = <T extends Record<string, any>>(items: T[], id?: string, pid?: string, children?: string, sort?: string) => ArrayToTreeResult<T>;
|
|
@@ -3,14 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 注意:types 模块不包含运行时实现;运行时请从 befly-shared/utils/* 引入。
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
6
|
export type KeyTransformer = (key: string) => string;
|
|
8
|
-
|
|
9
7
|
export type PresetTransform = "camel" | "snake" | "kebab" | "pascal" | "upper" | "lower";
|
|
10
|
-
|
|
11
8
|
export interface TransformOptions {
|
|
12
9
|
maxDepth?: number;
|
|
13
10
|
excludeKeys?: string[];
|
|
14
11
|
}
|
|
15
|
-
|
|
16
12
|
export type DeepTransformKeys = <T = any>(data: any, transformer: KeyTransformer | PresetTransform, options?: TransformOptions) => T;
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* scanViewsDir 相关纯函数的类型定义(类型模块,仅供 type 引用)。
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
4
|
export type ViewDirMeta = {
|
|
6
5
|
title: string;
|
|
7
6
|
order?: number;
|
|
8
7
|
};
|
|
9
|
-
|
|
10
8
|
export type MenuNodeLike<T> = {
|
|
11
9
|
name?: string;
|
|
12
10
|
path?: string;
|
|
13
11
|
sort?: number;
|
|
14
12
|
children?: T[];
|
|
15
13
|
};
|
|
16
|
-
|
|
17
14
|
export type CleanDirName = (name: string) => string;
|
|
18
|
-
|
|
19
15
|
export type NormalizeMenuPath = (path: string) => string;
|
|
20
|
-
|
|
21
16
|
export type NormalizeMenuTree = <T extends MenuNodeLike<T>>(menus: T[]) => T[];
|
|
22
|
-
|
|
23
17
|
export type ExtractScriptSetupBlock = (vueContent: string) => string | null;
|
|
24
|
-
|
|
25
18
|
export type ExtractDefinePageMetaFromScriptSetup = (scriptSetup: string) => ViewDirMeta | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ArrayToTreeResult<T extends Record<string, any>> = {
|
|
2
|
+
flat: Array<T>;
|
|
3
|
+
tree: Array<T>;
|
|
4
|
+
map: Map<string, T>;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* 将一维数组按 { id, pid } 组装为树形结构(纯函数 / 无副作用)。
|
|
8
|
+
*
|
|
9
|
+
* - 默认字段:id / pid / children / sort
|
|
10
|
+
* - pid 为空字符串或父节点不存在时,视为根节点
|
|
11
|
+
* - 内部会 clone 一份节点对象,并写入 children: []
|
|
12
|
+
* - 默认自带递归排序:按 sort 升序;sort 缺省/非法或 < 1 视为 999999;sort 相同按 id 自然序1
|
|
13
|
+
*/
|
|
14
|
+
export declare function arrayToTree<T extends Record<string, any>>(items: T[], id?: string, pid?: string, children?: string, sort?: string): ArrayToTreeResult<T>;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export type ArrayToTreeResult<T extends Record<string, any>> = {
|
|
2
|
-
flat: Array<T>;
|
|
3
|
-
tree: Array<T>;
|
|
4
|
-
map: Map<string, T>;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* 将一维数组按 { id, pid } 组装为树形结构(纯函数 / 无副作用)。
|
|
9
3
|
*
|
|
@@ -12,18 +6,15 @@ export type ArrayToTreeResult<T extends Record<string, any>> = {
|
|
|
12
6
|
* - 内部会 clone 一份节点对象,并写入 children: []
|
|
13
7
|
* - 默认自带递归排序:按 sort 升序;sort 缺省/非法或 < 1 视为 999999;sort 相同按 id 自然序1
|
|
14
8
|
*/
|
|
15
|
-
export function arrayToTree
|
|
9
|
+
export function arrayToTree(items, id = "id", pid = "pid", children = "children", sort = "sort") {
|
|
16
10
|
const idKey = typeof id === "string" && id.length > 0 ? id : "id";
|
|
17
11
|
const pidKey = typeof pid === "string" && pid.length > 0 ? pid : "pid";
|
|
18
12
|
const childrenKey = typeof children === "string" && children.length > 0 ? children : "children";
|
|
19
13
|
const sortKey = typeof sort === "string" && sort.length > 0 ? sort : "sort";
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const flat: T[] = [];
|
|
23
|
-
|
|
14
|
+
const map = new Map();
|
|
15
|
+
const flat = [];
|
|
24
16
|
const safeItems = Array.isArray(items) ? items : [];
|
|
25
|
-
|
|
26
|
-
const normalizeKey = (value: unknown): string => {
|
|
17
|
+
const normalizeKey = (value) => {
|
|
27
18
|
if (typeof value === "string") {
|
|
28
19
|
return value;
|
|
29
20
|
}
|
|
@@ -32,47 +23,36 @@ export function arrayToTree<T extends Record<string, any>>(items: T[], id: strin
|
|
|
32
23
|
}
|
|
33
24
|
return "";
|
|
34
25
|
};
|
|
35
|
-
|
|
36
26
|
for (const item of safeItems) {
|
|
37
|
-
const rawId = item ?
|
|
38
|
-
const rawPid = item ?
|
|
39
|
-
|
|
27
|
+
const rawId = item ? item[idKey] : undefined;
|
|
28
|
+
const rawPid = item ? item[pidKey] : undefined;
|
|
40
29
|
const normalizedId = normalizeKey(rawId);
|
|
41
30
|
const normalizedPid = normalizeKey(rawPid);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
(nextNode as any)[childrenKey] = [];
|
|
47
|
-
|
|
31
|
+
const nextNode = Object.assign({}, item);
|
|
32
|
+
nextNode[idKey] = normalizedId;
|
|
33
|
+
nextNode[pidKey] = normalizedPid;
|
|
34
|
+
nextNode[childrenKey] = [];
|
|
48
35
|
flat.push(nextNode);
|
|
49
|
-
|
|
50
36
|
if (normalizedId.length > 0) {
|
|
51
37
|
map.set(normalizedId, nextNode);
|
|
52
38
|
}
|
|
53
39
|
}
|
|
54
|
-
|
|
55
|
-
const tree: T[] = [];
|
|
56
|
-
|
|
40
|
+
const tree = [];
|
|
57
41
|
for (const node of flat) {
|
|
58
|
-
const selfId = normalizeKey(node ?
|
|
59
|
-
const parentId = normalizeKey(node ?
|
|
60
|
-
|
|
42
|
+
const selfId = normalizeKey(node ? node[idKey] : undefined);
|
|
43
|
+
const parentId = normalizeKey(node ? node[pidKey] : undefined);
|
|
61
44
|
if (parentId.length > 0 && parentId !== selfId) {
|
|
62
45
|
const parent = map.get(parentId);
|
|
63
|
-
if (parent && Array.isArray(
|
|
64
|
-
|
|
46
|
+
if (parent && Array.isArray(parent[childrenKey])) {
|
|
47
|
+
parent[childrenKey].push(node);
|
|
65
48
|
continue;
|
|
66
49
|
}
|
|
67
50
|
}
|
|
68
|
-
|
|
69
51
|
tree.push(node);
|
|
70
52
|
}
|
|
71
|
-
|
|
72
53
|
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const raw = node ? (node as any)[sortKey] : undefined;
|
|
54
|
+
const getSortValue = (node) => {
|
|
55
|
+
const raw = node ? node[sortKey] : undefined;
|
|
76
56
|
if (typeof raw !== "number") {
|
|
77
57
|
return 999999;
|
|
78
58
|
}
|
|
@@ -84,49 +64,38 @@ export function arrayToTree<T extends Record<string, any>>(items: T[], id: strin
|
|
|
84
64
|
}
|
|
85
65
|
return raw;
|
|
86
66
|
};
|
|
87
|
-
|
|
88
|
-
const compareNode = (a: T, b: T): number => {
|
|
67
|
+
const compareNode = (a, b) => {
|
|
89
68
|
const aSort = getSortValue(a);
|
|
90
69
|
const bSort = getSortValue(b);
|
|
91
|
-
|
|
92
70
|
if (aSort !== bSort) {
|
|
93
71
|
return aSort - bSort;
|
|
94
72
|
}
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
const bId = b ? (b as any)[idKey] : "";
|
|
98
|
-
|
|
73
|
+
const aId = a ? a[idKey] : "";
|
|
74
|
+
const bId = b ? b[idKey] : "";
|
|
99
75
|
return collator.compare(typeof aId === "string" ? aId : "", typeof bId === "string" ? bId : "");
|
|
100
76
|
};
|
|
101
|
-
|
|
102
|
-
const sortTreeInPlace = (nodes: Array<T>, seen: WeakSet<object>): void => {
|
|
77
|
+
const sortTreeInPlace = (nodes, seen) => {
|
|
103
78
|
if (!Array.isArray(nodes)) {
|
|
104
79
|
return;
|
|
105
80
|
}
|
|
106
|
-
|
|
107
81
|
if (nodes.length > 1) {
|
|
108
82
|
nodes.sort(compareNode);
|
|
109
83
|
}
|
|
110
|
-
|
|
111
84
|
for (const node of nodes) {
|
|
112
85
|
if (typeof node !== "object" || node === null) {
|
|
113
86
|
continue;
|
|
114
87
|
}
|
|
115
|
-
|
|
116
88
|
if (seen.has(node)) {
|
|
117
89
|
continue;
|
|
118
90
|
}
|
|
119
91
|
seen.add(node);
|
|
120
|
-
|
|
121
|
-
const childNodes = (node as any)[childrenKey];
|
|
92
|
+
const childNodes = node[childrenKey];
|
|
122
93
|
if (Array.isArray(childNodes) && childNodes.length > 0) {
|
|
123
94
|
sortTreeInPlace(childNodes, seen);
|
|
124
95
|
}
|
|
125
96
|
}
|
|
126
97
|
};
|
|
127
|
-
|
|
128
|
-
sortTreeInPlace(tree, new WeakSet<object>());
|
|
129
|
-
|
|
98
|
+
sortTreeInPlace(tree, new WeakSet());
|
|
130
99
|
return {
|
|
131
100
|
flat: flat,
|
|
132
101
|
tree: tree,
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 键名转换函数类型
|
|
3
|
+
*/
|
|
4
|
+
export type KeyTransformer = (key: string) => string;
|
|
5
|
+
/**
|
|
6
|
+
* 预设的转换方式
|
|
7
|
+
* - camel: 小驼峰 user_id → userId
|
|
8
|
+
* - snake: 下划线 userId → user_id
|
|
9
|
+
* - kebab: 短横线 userId → user-id
|
|
10
|
+
* - pascal: 大驼峰 user_id → UserId
|
|
11
|
+
* - upper: 大写 userId → USERID
|
|
12
|
+
* - lower: 小写 UserId → userid
|
|
13
|
+
*/
|
|
14
|
+
export type PresetTransform = "camel" | "snake" | "kebab" | "pascal" | "upper" | "lower";
|
|
15
|
+
/**
|
|
16
|
+
* 转换选项
|
|
17
|
+
*/
|
|
18
|
+
export interface TransformOptions {
|
|
19
|
+
/** 最大递归深度,默认 100。设置为 0 表示不限制深度(不推荐) */
|
|
20
|
+
maxDepth?: number;
|
|
21
|
+
/** 排除的键名列表,这些键名不会被转换 */
|
|
22
|
+
excludeKeys?: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 深度递归遍历数据结构,转换所有键名
|
|
26
|
+
* 支持嵌套对象和数组,自动防止循环引用和栈溢出
|
|
27
|
+
*
|
|
28
|
+
* @param data - 源数据(对象、数组或其他类型)
|
|
29
|
+
* @param transformer - 转换函数或预设方式
|
|
30
|
+
* @param options - 转换选项
|
|
31
|
+
* @returns 键名转换后的新数据
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // 小驼峰
|
|
35
|
+
* deepTransformKeys({ user_id: 123, user_info: { first_name: 'John' } }, 'camel')
|
|
36
|
+
* // { userId: 123, userInfo: { firstName: 'John' } }
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // 下划线
|
|
40
|
+
* deepTransformKeys({ userId: 123, userInfo: { firstName: 'John' } }, 'snake')
|
|
41
|
+
* // { user_id: 123, user_info: { first_name: 'John' } }
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // 短横线
|
|
45
|
+
* deepTransformKeys({ userId: 123, userInfo: { firstName: 'John' } }, 'kebab')
|
|
46
|
+
* // { 'user-id': 123, 'user-info': { 'first-name': 'John' } }
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // 大驼峰
|
|
50
|
+
* deepTransformKeys({ user_id: 123 }, 'pascal')
|
|
51
|
+
* // { UserId: 123 }
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // 大写
|
|
55
|
+
* deepTransformKeys({ userId: 123 }, 'upper')
|
|
56
|
+
* // { USERID: 123 }
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // 小写
|
|
60
|
+
* deepTransformKeys({ UserId: 123 }, 'lower')
|
|
61
|
+
* // { userid: 123 }
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // 自定义转换函数
|
|
65
|
+
* deepTransformKeys({ user_id: 123 }, (key) => `prefix_${key}`)
|
|
66
|
+
* // { prefix_user_id: 123 }
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // 限制递归深度
|
|
70
|
+
* deepTransformKeys(deepData, 'camel', { maxDepth: 10 })
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // 排除特定键名
|
|
74
|
+
* deepTransformKeys({ _id: '123', user_name: 'John' }, 'camel', { excludeKeys: ['_id'] })
|
|
75
|
+
* // { _id: '123', userName: 'John' }
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // 嵌套数组和对象
|
|
79
|
+
* deepTransformKeys({
|
|
80
|
+
* user_list: [{ user_id: 1, user_tags: [{ tag_name: 'vip' }] }]
|
|
81
|
+
* }, 'camel')
|
|
82
|
+
* // { userList: [{ userId: 1, userTags: [{ tagName: 'vip' }] }] }
|
|
83
|
+
*/
|
|
84
|
+
export declare const deepTransformKeys: <T = any>(data: any, transformer: KeyTransformer | PresetTransform, options?: TransformOptions) => T;
|
|
@@ -1,32 +1,5 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
2
|
import { camelCase, snakeCase, kebabCase, pascalCase } from "es-toolkit/string";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 键名转换函数类型
|
|
6
|
-
*/
|
|
7
|
-
export type KeyTransformer = (key: string) => string;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 预设的转换方式
|
|
11
|
-
* - camel: 小驼峰 user_id → userId
|
|
12
|
-
* - snake: 下划线 userId → user_id
|
|
13
|
-
* - kebab: 短横线 userId → user-id
|
|
14
|
-
* - pascal: 大驼峰 user_id → UserId
|
|
15
|
-
* - upper: 大写 userId → USERID
|
|
16
|
-
* - lower: 小写 UserId → userid
|
|
17
|
-
*/
|
|
18
|
-
export type PresetTransform = "camel" | "snake" | "kebab" | "pascal" | "upper" | "lower";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* 转换选项
|
|
22
|
-
*/
|
|
23
|
-
export interface TransformOptions {
|
|
24
|
-
/** 最大递归深度,默认 100。设置为 0 表示不限制深度(不推荐) */
|
|
25
|
-
maxDepth?: number;
|
|
26
|
-
/** 排除的键名列表,这些键名不会被转换 */
|
|
27
|
-
excludeKeys?: string[];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
3
|
/**
|
|
31
4
|
* 深度递归遍历数据结构,转换所有键名
|
|
32
5
|
* 支持嵌套对象和数组,自动防止循环引用和栈溢出
|
|
@@ -87,40 +60,41 @@ export interface TransformOptions {
|
|
|
87
60
|
* }, 'camel')
|
|
88
61
|
* // { userList: [{ userId: 1, userTags: [{ tagName: 'vip' }] }] }
|
|
89
62
|
*/
|
|
90
|
-
export const deepTransformKeys =
|
|
63
|
+
export const deepTransformKeys = (data, transformer, options = {}) => {
|
|
91
64
|
const { maxDepth = 100, excludeKeys = [] } = options;
|
|
92
|
-
|
|
93
65
|
// 获取实际的转换函数
|
|
94
|
-
let transformFn
|
|
66
|
+
let transformFn;
|
|
95
67
|
if (typeof transformer === "function") {
|
|
96
68
|
transformFn = transformer;
|
|
97
|
-
}
|
|
69
|
+
}
|
|
70
|
+
else if (transformer === "camel") {
|
|
98
71
|
transformFn = camelCase;
|
|
99
|
-
}
|
|
72
|
+
}
|
|
73
|
+
else if (transformer === "snake") {
|
|
100
74
|
transformFn = snakeCase;
|
|
101
|
-
}
|
|
75
|
+
}
|
|
76
|
+
else if (transformer === "kebab") {
|
|
102
77
|
transformFn = kebabCase;
|
|
103
|
-
}
|
|
78
|
+
}
|
|
79
|
+
else if (transformer === "pascal") {
|
|
104
80
|
transformFn = pascalCase;
|
|
105
|
-
} else if (transformer === "upper") {
|
|
106
|
-
transformFn = (key: string) => key.toUpperCase();
|
|
107
|
-
} else {
|
|
108
|
-
transformFn = (key: string) => key.toLowerCase();
|
|
109
81
|
}
|
|
110
|
-
|
|
82
|
+
else if (transformer === "upper") {
|
|
83
|
+
transformFn = (key) => key.toUpperCase();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
transformFn = (key) => key.toLowerCase();
|
|
87
|
+
}
|
|
111
88
|
// 用于检测循环引用的 WeakSet
|
|
112
89
|
const visited = new WeakSet();
|
|
113
|
-
|
|
114
90
|
// 创建排除键名集合,提升查找性能
|
|
115
91
|
const excludeSet = new Set(excludeKeys);
|
|
116
|
-
|
|
117
92
|
// 递归转换函数
|
|
118
|
-
const transform = (value
|
|
93
|
+
const transform = (value, depth) => {
|
|
119
94
|
// 处理 null 和 undefined
|
|
120
95
|
if (value === null || value === undefined) {
|
|
121
96
|
return value;
|
|
122
97
|
}
|
|
123
|
-
|
|
124
98
|
// 处理数组
|
|
125
99
|
if (Array.isArray(value)) {
|
|
126
100
|
// 检测循环引用
|
|
@@ -128,18 +102,15 @@ export const deepTransformKeys = <T = any>(data: any, transformer: KeyTransforme
|
|
|
128
102
|
return value;
|
|
129
103
|
}
|
|
130
104
|
visited.add(value);
|
|
131
|
-
|
|
132
105
|
// 检查深度限制:如果达到限制,返回原数组
|
|
133
106
|
if (maxDepth > 0 && depth >= maxDepth) {
|
|
134
107
|
visited.delete(value);
|
|
135
108
|
return value;
|
|
136
109
|
}
|
|
137
|
-
|
|
138
|
-
const result = value.map((item: any) => transform(item, depth + 1));
|
|
110
|
+
const result = value.map((item) => transform(item, depth + 1));
|
|
139
111
|
visited.delete(value);
|
|
140
112
|
return result;
|
|
141
113
|
}
|
|
142
|
-
|
|
143
114
|
// 处理对象
|
|
144
115
|
if (isPlainObject(value)) {
|
|
145
116
|
// 检测循环引用
|
|
@@ -147,14 +118,12 @@ export const deepTransformKeys = <T = any>(data: any, transformer: KeyTransforme
|
|
|
147
118
|
return value;
|
|
148
119
|
}
|
|
149
120
|
visited.add(value);
|
|
150
|
-
|
|
151
121
|
// 检查深度限制:如果达到限制,返回原对象
|
|
152
122
|
if (maxDepth > 0 && depth >= maxDepth) {
|
|
153
123
|
visited.delete(value);
|
|
154
124
|
return value;
|
|
155
125
|
}
|
|
156
|
-
|
|
157
|
-
const result: any = {};
|
|
126
|
+
const result = {};
|
|
158
127
|
for (const [key, val] of Object.entries(value)) {
|
|
159
128
|
// 检查是否在排除列表中
|
|
160
129
|
const transformedKey = excludeSet.has(key) ? key : transformFn(key);
|
|
@@ -163,10 +132,8 @@ export const deepTransformKeys = <T = any>(data: any, transformer: KeyTransforme
|
|
|
163
132
|
visited.delete(value);
|
|
164
133
|
return result;
|
|
165
134
|
}
|
|
166
|
-
|
|
167
135
|
// 其他类型(字符串、数字、布尔值等)直接返回
|
|
168
136
|
return value;
|
|
169
137
|
};
|
|
170
|
-
|
|
171
|
-
return transform(data, 0) as T;
|
|
138
|
+
return transform(data, 0);
|
|
172
139
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface FieldClearOptions {
|
|
2
|
+
pickKeys?: string[];
|
|
3
|
+
omitKeys?: string[];
|
|
4
|
+
keepValues?: any[];
|
|
5
|
+
excludeValues?: any[];
|
|
6
|
+
keepMap?: Record<string, any>;
|
|
7
|
+
}
|
|
8
|
+
export type FieldClearResult<T> = T extends Array<infer U> ? Array<FieldClearResult<U>> : T extends object ? {
|
|
9
|
+
[K in keyof T]?: T[K];
|
|
10
|
+
} : T;
|
|
11
|
+
export declare function fieldClear<T = any>(data: T | T[], options?: FieldClearOptions): FieldClearResult<T>;
|
|
@@ -1,40 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* @property {string[]=} pickKeys
|
|
4
|
-
* @property {string[]=} omitKeys
|
|
5
|
-
* @property {any[]=} keepValues
|
|
6
|
-
* @property {any[]=} excludeValues
|
|
7
|
-
* @property {Record<string, any>=} keepMap
|
|
8
|
-
*/
|
|
9
|
-
|
|
1
|
+
// fieldClear 工具函数实现(shared 版)
|
|
2
|
+
// 支持 pick/omit/keepValues/excludeValues/keepMap,处理对象和数组
|
|
10
3
|
function isObject(val) {
|
|
11
4
|
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
12
5
|
}
|
|
13
|
-
|
|
14
6
|
function isArray(val) {
|
|
15
7
|
return Array.isArray(val);
|
|
16
8
|
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 清理对象/数组字段
|
|
20
|
-
* - 支持 pick/omit/keepValues/excludeValues
|
|
21
|
-
* - 支持 keepMap 强制保留
|
|
22
|
-
* @template T
|
|
23
|
-
* @param {T|T[]} data
|
|
24
|
-
* @param {FieldClearOptions=} options
|
|
25
|
-
* @returns {any}
|
|
26
|
-
*/
|
|
27
9
|
export function fieldClear(data, options = {}) {
|
|
28
10
|
const pickKeys = options.pickKeys;
|
|
29
11
|
const omitKeys = options.omitKeys;
|
|
30
12
|
const keepValues = options.keepValues;
|
|
31
13
|
const excludeValues = options.excludeValues;
|
|
32
14
|
const keepMap = options.keepMap;
|
|
33
|
-
|
|
34
15
|
const filterObj = (obj) => {
|
|
35
|
-
/** @type {Record<string, any>} */
|
|
36
16
|
const result = {};
|
|
37
|
-
|
|
38
17
|
let keys = Object.keys(obj);
|
|
39
18
|
if (pickKeys && pickKeys.length) {
|
|
40
19
|
keys = keys.filter((k) => pickKeys.includes(k));
|
|
@@ -42,10 +21,8 @@ export function fieldClear(data, options = {}) {
|
|
|
42
21
|
if (omitKeys && omitKeys.length) {
|
|
43
22
|
keys = keys.filter((k) => !omitKeys.includes(k));
|
|
44
23
|
}
|
|
45
|
-
|
|
46
24
|
for (const key of keys) {
|
|
47
25
|
const value = obj[key];
|
|
48
|
-
|
|
49
26
|
// 1. keepMap 优先
|
|
50
27
|
if (keepMap && Object.prototype.hasOwnProperty.call(keepMap, key)) {
|
|
51
28
|
if (Object.is(keepMap[key], value)) {
|
|
@@ -53,42 +30,35 @@ export function fieldClear(data, options = {}) {
|
|
|
53
30
|
continue;
|
|
54
31
|
}
|
|
55
32
|
}
|
|
56
|
-
|
|
57
33
|
// 2. keepValues
|
|
58
34
|
if (keepValues && keepValues.length && !keepValues.includes(value)) {
|
|
59
35
|
continue;
|
|
60
36
|
}
|
|
61
|
-
|
|
62
37
|
// 3. excludeValues
|
|
63
38
|
if (excludeValues && excludeValues.length && excludeValues.includes(value)) {
|
|
64
39
|
continue;
|
|
65
40
|
}
|
|
66
|
-
|
|
67
41
|
result[key] = value;
|
|
68
42
|
}
|
|
69
|
-
|
|
70
43
|
return result;
|
|
71
44
|
};
|
|
72
|
-
|
|
73
45
|
if (isArray(data)) {
|
|
74
46
|
return data
|
|
75
47
|
.map((item) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
48
|
+
if (isObject(item)) {
|
|
49
|
+
return filterObj(item);
|
|
50
|
+
}
|
|
51
|
+
return item;
|
|
52
|
+
})
|
|
81
53
|
.filter((item) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
54
|
+
if (isObject(item)) {
|
|
55
|
+
return Object.keys(item).length > 0;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
87
59
|
}
|
|
88
|
-
|
|
89
60
|
if (isObject(data)) {
|
|
90
61
|
return filterObj(data);
|
|
91
62
|
}
|
|
92
|
-
|
|
93
63
|
return data;
|
|
94
64
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 密码哈希工具
|
|
3
|
+
* 使用 SHA-256 + 盐值对密码进行单向哈希
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 使用 SHA-256 对密码进行哈希
|
|
7
|
+
* @param password - 原始密码
|
|
8
|
+
* @param salt - 盐值,默认为 befly
|
|
9
|
+
* @returns 哈希后的密码(十六进制字符串)
|
|
10
|
+
*/
|
|
11
|
+
export declare function hashPassword(password: string, salt?: string): Promise<string>;
|
|
@@ -2,26 +2,21 @@
|
|
|
2
2
|
* 密码哈希工具
|
|
3
3
|
* 使用 SHA-256 + 盐值对密码进行单向哈希
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* 使用 SHA-256 对密码进行哈希
|
|
8
7
|
* @param password - 原始密码
|
|
9
8
|
* @param salt - 盐值,默认为 befly
|
|
10
9
|
* @returns 哈希后的密码(十六进制字符串)
|
|
11
10
|
*/
|
|
12
|
-
export async function hashPassword(password
|
|
11
|
+
export async function hashPassword(password, salt = "befly") {
|
|
13
12
|
const data = password + salt;
|
|
14
|
-
|
|
15
13
|
// 将字符串转换为 Uint8Array
|
|
16
14
|
const encoder = new TextEncoder();
|
|
17
15
|
const dataBuffer = encoder.encode(data);
|
|
18
|
-
|
|
19
16
|
// 使用 Web Crypto API 进行 SHA-256 哈希
|
|
20
17
|
const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
|
|
21
|
-
|
|
22
18
|
// 将 ArrayBuffer 转换为十六进制字符串
|
|
23
19
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
24
20
|
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
25
|
-
|
|
26
21
|
return hashHex;
|
|
27
22
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将未知输入规范化为 pathname 字符串数组。
|
|
3
|
+
*
|
|
4
|
+
* 规则(契约):
|
|
5
|
+
* - value 为“假值”(null/undefined/""/0/false/NaN)时返回空数组 []。
|
|
6
|
+
* - value 必须是 string[],否则抛错:`${fieldLabel} 必须是字符串数组`。
|
|
7
|
+
* - 数组元素必须满足:
|
|
8
|
+
* - 类型为 string
|
|
9
|
+
* - 不允许为空字符串
|
|
10
|
+
* - 不允许包含任何空白字符(空格/制表符/换行等)
|
|
11
|
+
* - 必须以 "/" 开头(pathname)
|
|
12
|
+
* - forbidMethodPrefix=true 时,禁止 "GET/POST/..." 等 method 前缀,并给出更明确的错误提示。
|
|
13
|
+
*
|
|
14
|
+
* 注意:该函数不会做任何隐式修复/转换(例如 trim/split/JSON.parse/去重/排序)。
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizePathnameListInput(value: unknown, fieldLabel: string, forbidMethodPrefix: boolean): string[];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const HTTP_METHOD_PREFIX_RE = /^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/i;
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* 将未知输入规范化为 pathname 字符串数组。
|
|
5
4
|
*
|
|
@@ -15,44 +14,35 @@ const HTTP_METHOD_PREFIX_RE = /^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/i;
|
|
|
15
14
|
*
|
|
16
15
|
* 注意:该函数不会做任何隐式修复/转换(例如 trim/split/JSON.parse/去重/排序)。
|
|
17
16
|
*/
|
|
18
|
-
export function normalizePathnameListInput(value
|
|
17
|
+
export function normalizePathnameListInput(value, fieldLabel, forbidMethodPrefix) {
|
|
19
18
|
// “假值”统一视为空数组:null/undefined/""/0/false/NaN
|
|
20
|
-
if (!value)
|
|
21
|
-
|
|
19
|
+
if (!value)
|
|
20
|
+
return [];
|
|
22
21
|
if (!Array.isArray(value)) {
|
|
23
22
|
throw new Error(`${fieldLabel} 必须是字符串数组`);
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
const out: string[] = [];
|
|
27
|
-
|
|
24
|
+
const out = [];
|
|
28
25
|
for (let i = 0; i < value.length; i += 1) {
|
|
29
26
|
const item = value[i];
|
|
30
27
|
const itemLabel = `${fieldLabel}[${i}]`;
|
|
31
|
-
|
|
32
28
|
if (typeof item !== "string") {
|
|
33
29
|
throw new Error(`${itemLabel} 必须是字符串`);
|
|
34
30
|
}
|
|
35
|
-
|
|
36
31
|
if (item.length === 0) {
|
|
37
32
|
throw new Error(`${itemLabel} 不允许为空字符串`);
|
|
38
33
|
}
|
|
39
|
-
|
|
40
34
|
// 优先给出 method 前缀提示(更明确)
|
|
41
35
|
if (forbidMethodPrefix && HTTP_METHOD_PREFIX_RE.test(item)) {
|
|
42
36
|
throw new Error(`${itemLabel} 不允许包含 method 前缀,应为 url.pathname(例如 /api/app/xxx)`);
|
|
43
37
|
}
|
|
44
|
-
|
|
45
38
|
// 不做 trim 自动转换:含任何空白字符都视为不合法
|
|
46
39
|
if (/\s/.test(item)) {
|
|
47
40
|
throw new Error(`${itemLabel} 不允许包含空白字符(空格/制表符/换行等)`);
|
|
48
41
|
}
|
|
49
|
-
|
|
50
42
|
if (!item.startsWith("/")) {
|
|
51
43
|
throw new Error(`${itemLabel} 必须是 pathname(以 / 开头)`);
|
|
52
44
|
}
|
|
53
|
-
|
|
54
45
|
out.push(item);
|
|
55
46
|
}
|
|
56
|
-
|
|
57
47
|
return out;
|
|
58
48
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type ViewDirMeta = {
|
|
2
|
+
title: string;
|
|
3
|
+
order?: number;
|
|
4
|
+
};
|
|
5
|
+
type MenuNodeLike<T> = {
|
|
6
|
+
name?: string;
|
|
7
|
+
path?: string;
|
|
8
|
+
sort?: number;
|
|
9
|
+
children?: T[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* 清理目录名中的数字后缀
|
|
13
|
+
* 如:login_1 → login, index_2 → index
|
|
14
|
+
*/
|
|
15
|
+
export declare function cleanDirName(name: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* 约束:统一 path 形态,避免隐藏菜单匹配、DB 同步出现重复
|
|
18
|
+
* - 必须以 / 开头
|
|
19
|
+
* - 折叠多个 /
|
|
20
|
+
* - 去掉尾随 /(根 / 除外)
|
|
21
|
+
*/
|
|
22
|
+
export declare function normalizeMenuPath(path: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* 递归规范化并按 path 去重(同 path 的 children 合并)
|
|
25
|
+
*
|
|
26
|
+
* 说明:该函数是纯函数,不依赖任何运行时环境;会返回新数组,但会在内部对克隆对象做合并赋值。
|
|
27
|
+
*/
|
|
28
|
+
export declare function normalizeMenuTree<T extends MenuNodeLike<T>>(menus: T[]): T[];
|
|
29
|
+
/**
|
|
30
|
+
* 只取第一个 <script ... setup ...> 块
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractScriptSetupBlock(vueContent: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* 从 <script setup> 中提取 definePage({ meta })
|
|
35
|
+
*
|
|
36
|
+
* 简化约束:
|
|
37
|
+
* - 每个页面只有一个 definePage
|
|
38
|
+
* - title 是纯字符串字面量
|
|
39
|
+
* - order 是数字字面量(可选)
|
|
40
|
+
* - 不考虑变量/表达式/多段 meta 组合
|
|
41
|
+
*/
|
|
42
|
+
export declare function extractDefinePageMetaFromScriptSetup(scriptSetup: string): ViewDirMeta | null;
|
|
43
|
+
export {};
|
|
@@ -1,129 +1,97 @@
|
|
|
1
|
-
export type ViewDirMeta = {
|
|
2
|
-
title: string;
|
|
3
|
-
order?: number;
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
type MenuNodeLike<T> = {
|
|
7
|
-
name?: string;
|
|
8
|
-
path?: string;
|
|
9
|
-
sort?: number;
|
|
10
|
-
children?: T[];
|
|
11
|
-
};
|
|
12
|
-
|
|
13
1
|
/**
|
|
14
2
|
* 清理目录名中的数字后缀
|
|
15
3
|
* 如:login_1 → login, index_2 → index
|
|
16
4
|
*/
|
|
17
|
-
export function cleanDirName(name
|
|
5
|
+
export function cleanDirName(name) {
|
|
18
6
|
return name.replace(/_\d+$/, "");
|
|
19
7
|
}
|
|
20
|
-
|
|
21
8
|
/**
|
|
22
9
|
* 约束:统一 path 形态,避免隐藏菜单匹配、DB 同步出现重复
|
|
23
10
|
* - 必须以 / 开头
|
|
24
11
|
* - 折叠多个 /
|
|
25
12
|
* - 去掉尾随 /(根 / 除外)
|
|
26
13
|
*/
|
|
27
|
-
export function normalizeMenuPath(path
|
|
14
|
+
export function normalizeMenuPath(path) {
|
|
28
15
|
let result = path;
|
|
29
|
-
|
|
30
16
|
if (!result) {
|
|
31
17
|
return "/";
|
|
32
18
|
}
|
|
33
|
-
|
|
34
19
|
if (!result.startsWith("/")) {
|
|
35
20
|
result = `/${result}`;
|
|
36
21
|
}
|
|
37
|
-
|
|
38
22
|
result = result.replace(/\/+/g, "/");
|
|
39
|
-
|
|
40
23
|
if (result.length > 1) {
|
|
41
24
|
result = result.replace(/\/+$/, "");
|
|
42
25
|
}
|
|
43
|
-
|
|
44
26
|
return result;
|
|
45
27
|
}
|
|
46
|
-
|
|
47
28
|
/**
|
|
48
29
|
* 递归规范化并按 path 去重(同 path 的 children 合并)
|
|
49
30
|
*
|
|
50
31
|
* 说明:该函数是纯函数,不依赖任何运行时环境;会返回新数组,但会在内部对克隆对象做合并赋值。
|
|
51
32
|
*/
|
|
52
|
-
export function normalizeMenuTree
|
|
53
|
-
const map = new Map
|
|
54
|
-
|
|
33
|
+
export function normalizeMenuTree(menus) {
|
|
34
|
+
const map = new Map();
|
|
55
35
|
for (const menu of menus) {
|
|
56
36
|
const rawPath = menu.path;
|
|
57
37
|
const menuPath = rawPath ? normalizeMenuPath(rawPath) : "";
|
|
58
|
-
|
|
59
38
|
if (!menuPath) {
|
|
60
39
|
continue;
|
|
61
40
|
}
|
|
62
|
-
|
|
63
41
|
// 不使用 structuredClone:
|
|
64
42
|
// - 结构中可能出现函数/类实例等不可 clone 的值
|
|
65
43
|
// - 这里我们只需要“保留额外字段 + 递归 children 生成新数组”
|
|
66
44
|
// 用浅拷贝即可满足需求
|
|
67
|
-
const cloned = Object.assign({}, menu)
|
|
68
|
-
|
|
69
|
-
|
|
45
|
+
const cloned = Object.assign({}, menu);
|
|
46
|
+
cloned.path = menuPath;
|
|
70
47
|
const rawChildren = menu.children;
|
|
71
48
|
if (rawChildren && rawChildren.length > 0) {
|
|
72
|
-
|
|
49
|
+
cloned.children = normalizeMenuTree(rawChildren);
|
|
73
50
|
}
|
|
74
|
-
|
|
75
51
|
const existing = map.get(menuPath);
|
|
76
52
|
if (existing) {
|
|
77
|
-
const clonedChildren =
|
|
53
|
+
const clonedChildren = cloned.children;
|
|
78
54
|
if (clonedChildren && clonedChildren.length > 0) {
|
|
79
|
-
let existingChildren =
|
|
55
|
+
let existingChildren = existing.children;
|
|
80
56
|
if (!existingChildren) {
|
|
81
57
|
existingChildren = [];
|
|
82
|
-
|
|
58
|
+
existing.children = existingChildren;
|
|
83
59
|
}
|
|
84
|
-
|
|
85
60
|
for (const child of clonedChildren) {
|
|
86
61
|
existingChildren.push(child);
|
|
87
62
|
}
|
|
88
|
-
|
|
89
|
-
(existing as any).children = normalizeMenuTree(existingChildren);
|
|
63
|
+
existing.children = normalizeMenuTree(existingChildren);
|
|
90
64
|
}
|
|
91
|
-
|
|
92
65
|
if (typeof cloned.sort === "number") {
|
|
93
|
-
|
|
66
|
+
existing.sort = cloned.sort;
|
|
94
67
|
}
|
|
95
|
-
|
|
96
68
|
if (typeof cloned.name === "string" && cloned.name) {
|
|
97
|
-
|
|
69
|
+
existing.name = cloned.name;
|
|
98
70
|
}
|
|
99
|
-
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
100
73
|
map.set(menuPath, cloned);
|
|
101
74
|
}
|
|
102
75
|
}
|
|
103
|
-
|
|
104
76
|
const result = Array.from(map.values());
|
|
105
|
-
result.sort((a, b) => (
|
|
77
|
+
result.sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999));
|
|
106
78
|
return result;
|
|
107
79
|
}
|
|
108
|
-
|
|
109
80
|
/**
|
|
110
81
|
* 只取第一个 <script ... setup ...> 块
|
|
111
82
|
*/
|
|
112
|
-
export function extractScriptSetupBlock(vueContent
|
|
83
|
+
export function extractScriptSetupBlock(vueContent) {
|
|
113
84
|
const openTag = /<script\b[^>]*\bsetup\b[^>]*>/i.exec(vueContent);
|
|
114
85
|
if (!openTag) {
|
|
115
86
|
return null;
|
|
116
87
|
}
|
|
117
|
-
|
|
118
88
|
const start = openTag.index + openTag[0].length;
|
|
119
89
|
const closeIndex = vueContent.indexOf("</script>", start);
|
|
120
90
|
if (closeIndex < 0) {
|
|
121
91
|
return null;
|
|
122
92
|
}
|
|
123
|
-
|
|
124
93
|
return vueContent.slice(start, closeIndex);
|
|
125
94
|
}
|
|
126
|
-
|
|
127
95
|
/**
|
|
128
96
|
* 从 <script setup> 中提取 definePage({ meta })
|
|
129
97
|
*
|
|
@@ -133,14 +101,12 @@ export function extractScriptSetupBlock(vueContent: string): string | null {
|
|
|
133
101
|
* - order 是数字字面量(可选)
|
|
134
102
|
* - 不考虑变量/表达式/多段 meta 组合
|
|
135
103
|
*/
|
|
136
|
-
export function extractDefinePageMetaFromScriptSetup(scriptSetup
|
|
104
|
+
export function extractDefinePageMetaFromScriptSetup(scriptSetup) {
|
|
137
105
|
const titleMatch = scriptSetup.match(/definePage\s*\([\s\S]*?meta\s*:\s*\{[\s\S]*?title\s*:\s*(["'`])([^"'`]+)\1/);
|
|
138
106
|
if (!titleMatch) {
|
|
139
107
|
return null;
|
|
140
108
|
}
|
|
141
|
-
|
|
142
109
|
const orderMatch = scriptSetup.match(/definePage\s*\([\s\S]*?meta\s*:\s*\{[\s\S]*?order\s*:\s*(\d+)/);
|
|
143
|
-
|
|
144
110
|
return {
|
|
145
111
|
title: titleMatch[2],
|
|
146
112
|
order: orderMatch ? Number(orderMatch[1]) : undefined
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 为表格列添加默认配置(纯函数)。
|
|
3
|
+
*
|
|
4
|
+
* 设计目标:
|
|
5
|
+
* - 不依赖浏览器/Node 特定 API
|
|
6
|
+
* - 不修改入参 columns 的对象引用(会返回新数组,并对每个列对象做浅拷贝合并)
|
|
7
|
+
*
|
|
8
|
+
* 默认行为:
|
|
9
|
+
* - base 默认:{ width: 200, ellipsis: true }
|
|
10
|
+
* - 特殊列:operation/state/id
|
|
11
|
+
* - colKey 以 At/At2 结尾时:默认 { align: "center" }
|
|
12
|
+
* - customConfig 可覆盖/扩展默认 specialColumnConfig(浅合并/替换:若传入 operation 配置,将整体替换默认 operation 配置,默认 fixed="right" 不会被保留)
|
|
13
|
+
*/
|
|
14
|
+
export declare function withDefaultColumns(columns: any[], customConfig?: Record<string, any>): any[];
|
|
@@ -11,38 +11,28 @@
|
|
|
11
11
|
* - colKey 以 At/At2 结尾时:默认 { align: "center" }
|
|
12
12
|
* - customConfig 可覆盖/扩展默认 specialColumnConfig(浅合并/替换:若传入 operation 配置,将整体替换默认 operation 配置,默认 fixed="right" 不会被保留)
|
|
13
13
|
*/
|
|
14
|
-
export function withDefaultColumns(columns
|
|
14
|
+
export function withDefaultColumns(columns, customConfig) {
|
|
15
15
|
const safeColumns = Array.isArray(columns) ? columns : [];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
id: { width: 200, align: "center" }
|
|
22
|
-
},
|
|
23
|
-
customConfig || {}
|
|
24
|
-
);
|
|
25
|
-
|
|
16
|
+
const specialColumnConfig = Object.assign({
|
|
17
|
+
operation: { width: 100, align: "center", fixed: "right" },
|
|
18
|
+
state: { width: 100, align: "center" },
|
|
19
|
+
id: { width: 200, align: "center" }
|
|
20
|
+
}, customConfig || {});
|
|
26
21
|
return safeColumns.map((col) => {
|
|
27
|
-
const colKey = col &&
|
|
28
|
-
|
|
22
|
+
const colKey = col && col.colKey;
|
|
29
23
|
let specialConfig = colKey ? specialColumnConfig[colKey] : undefined;
|
|
30
|
-
|
|
31
24
|
if (!specialConfig && typeof colKey === "string" && (colKey.endsWith("At") || colKey.endsWith("At2"))) {
|
|
32
25
|
specialConfig = { align: "center" };
|
|
33
26
|
}
|
|
34
|
-
|
|
35
27
|
const base = {
|
|
36
28
|
width: 200,
|
|
37
29
|
ellipsis: true
|
|
38
30
|
};
|
|
39
|
-
|
|
40
31
|
const merged = Object.assign({}, base);
|
|
41
32
|
if (specialConfig) {
|
|
42
33
|
Object.assign(merged, specialConfig);
|
|
43
34
|
}
|
|
44
35
|
Object.assign(merged, col);
|
|
45
|
-
|
|
46
36
|
return merged;
|
|
47
37
|
});
|
|
48
38
|
}
|
package/package.json
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-shared",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"gitHead": "
|
|
3
|
+
"version": "1.3.11",
|
|
4
|
+
"gitHead": "0914e9a6efb3f41136034f14d5f9cda4d5e7c3d0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Befly 纯函数与纯常量共享包(不包含 Node/浏览器特定实现)",
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
8
|
"author": "chensuiyi <bimostyle@qq.com>",
|
|
9
9
|
"files": [
|
|
10
|
-
"
|
|
11
|
-
"types/",
|
|
12
|
-
"utils/"
|
|
10
|
+
"dist/"
|
|
13
11
|
],
|
|
14
12
|
"type": "module",
|
|
15
13
|
"exports": {
|
|
16
|
-
"./utils/*":
|
|
17
|
-
|
|
14
|
+
"./utils/*": {
|
|
15
|
+
"types": "./dist/utils/*.d.ts",
|
|
16
|
+
"default": "./dist/utils/*.js"
|
|
17
|
+
},
|
|
18
|
+
"./types/*": {
|
|
19
|
+
"types": "./dist/types/*.d.ts",
|
|
20
|
+
"default": "./dist/types/*.js"
|
|
21
|
+
}
|
|
18
22
|
},
|
|
19
23
|
"scripts": {
|
|
20
|
-
"
|
|
24
|
+
"clean": "rimraf dist",
|
|
25
|
+
"build": "bunx tsgo -p tsconfig.build.json",
|
|
26
|
+
"test": "bun test",
|
|
27
|
+
"prepack": "bun run build && bun run ./scripts/ensureDist.ts",
|
|
28
|
+
"typecheck": "bunx tsgo -p tsconfig.json --noEmit"
|
|
21
29
|
},
|
|
22
30
|
"dependencies": {
|
|
23
31
|
"es-toolkit": "^1.43.0"
|