@jiangood/springboot-admin-starter 0.0.2 → 0.0.4
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/config/dist/config.js +32 -0
- package/config/dist/index.js +0 -1
- package/config/dist/plugins/form-plugin.js +2 -2
- package/package.json +12 -10
- package/src/forms/demoForm.jsx +5 -5
- package/src/framework/components/DownloadFileButton.jsx +2 -2
- package/src/framework/components/Ellipsis.jsx +2 -2
- package/src/framework/components/LinkButton.d.ts +4 -4
- package/src/framework/components/LinkButton.jsx +6 -9
- package/src/framework/components/Page/index.jsx +2 -2
- package/src/framework/components/PageLoading.tsx +27 -0
- package/src/framework/components/ProTable/components/ToolBar/index.jsx +6 -7
- package/src/framework/components/ProTable/index.d.ts +5 -1
- package/src/framework/components/ProTable/index.jsx +4 -6
- package/src/framework/components/ProTable/index.less +0 -1
- package/src/framework/components/ValueType/registry.jsx +9 -20
- package/src/framework/components/{index.js → index.ts} +2 -10
- package/src/framework/components/system/ButtonList.jsx +35 -0
- package/src/framework/components/system/HasPerm.tsx +14 -0
- package/src/framework/components/system/index.tsx +29 -0
- package/src/framework/components/view/ViewBooleanEnableDisable.tsx +20 -0
- package/src/framework/components/view/ViewEllipsis.jsx +2 -2
- package/src/framework/components/view/ViewFile.jsx +1 -2
- package/src/framework/components/view/ViewImage.jsx +2 -3
- package/src/framework/components/view/ViewText.tsx +16 -0
- package/src/framework/components/view/{index.js → index.ts} +0 -2
- package/src/framework/field-components/FieldBoolean.d.ts +13 -0
- package/src/framework/field-components/FieldBoolean.jsx +76 -0
- package/src/framework/field-components/FieldDate.d.ts +25 -0
- package/src/framework/field-components/FieldDate.jsx +109 -0
- package/src/framework/field-components/FieldDateRange.d.ts +6 -0
- package/src/framework/field-components/FieldDateRange.jsx +101 -0
- package/src/framework/field-components/FieldDictSelect.d.ts +13 -0
- package/src/framework/field-components/FieldDictSelect.jsx +16 -0
- package/src/framework/field-components/FieldEditor.d.ts +10 -0
- package/src/framework/field-components/FieldEditor.jsx +58 -0
- package/src/framework/field-components/FieldPercent.d.ts +12 -0
- package/src/framework/field-components/FieldPercent.jsx +27 -0
- package/src/framework/field-components/FieldRemoteSelect.d.ts +13 -0
- package/src/framework/field-components/FieldRemoteSelect.jsx +87 -0
- package/src/framework/field-components/FieldRemoteSelectMultiple.d.ts +13 -0
- package/src/framework/field-components/FieldRemoteSelectMultiple.jsx +86 -0
- package/src/framework/field-components/FieldRemoteSelectMultipleInline.d.ts +20 -0
- package/src/framework/field-components/FieldRemoteSelectMultipleInline.jsx +86 -0
- package/src/framework/field-components/FieldRemoteTree.d.ts +21 -0
- package/src/framework/field-components/FieldRemoteTree.jsx +45 -0
- package/src/framework/field-components/FieldRemoteTreeCascader.d.ts +23 -0
- package/src/framework/field-components/FieldRemoteTreeCascader.jsx +61 -0
- package/src/framework/field-components/FieldRemoteTreeSelect.d.ts +17 -0
- package/src/framework/field-components/FieldRemoteTreeSelect.jsx +67 -0
- package/src/framework/field-components/FieldRemoteTreeSelectMultiple.d.ts +17 -0
- package/src/framework/field-components/FieldRemoteTreeSelectMultiple.jsx +68 -0
- package/src/framework/field-components/FieldSysOrgTree.d.ts +12 -0
- package/src/framework/field-components/FieldSysOrgTree.jsx +23 -0
- package/src/framework/field-components/FieldSysOrgTreeSelect.d.ts +12 -0
- package/src/framework/field-components/FieldSysOrgTreeSelect.jsx +23 -0
- package/src/framework/{components/field/FieldEditTable/index.d.ts → field-components/FieldTable.d.ts} +5 -2
- package/src/framework/{components/field/FieldEditTable/index.jsx → field-components/FieldTable.jsx} +29 -21
- package/src/framework/{components/field/select/FieldTableSelect/index.d.ts → field-components/FieldTableSelect.d.ts} +4 -14
- package/src/framework/field-components/FieldTableSelect.jsx +59 -0
- package/src/framework/{components/field/upload → field-components}/FieldUploadFile.d.ts +0 -1
- package/src/framework/{components/field/upload → field-components}/FieldUploadFile.jsx +5 -9
- package/src/framework/field-components/index.ts +20 -0
- package/src/framework/{components/OrgTree.jsx → field-components/system/OrgTree.tsx} +8 -7
- package/src/framework/{components/RoleTree.jsx → field-components/system/RoleTree.tsx} +3 -3
- package/src/framework/field-components/system/index.ts +2 -0
- package/src/framework/index.ts +4 -0
- package/src/framework/utils/ArrUtils.ts +229 -0
- package/src/framework/utils/ColorsUtils.ts +378 -0
- package/src/framework/utils/{date.js → DateUtils.ts} +69 -69
- package/src/framework/utils/DeviceUtils.ts +46 -0
- package/src/framework/utils/DomUtils.ts +50 -0
- package/src/framework/utils/EventBusUtils.ts +143 -0
- package/src/framework/utils/MessageUtils.tsx +182 -0
- package/src/framework/utils/ObjectUtils.ts +113 -0
- package/src/framework/utils/StorageUtils.ts +67 -0
- package/src/framework/utils/StringUtils.ts +410 -0
- package/src/framework/utils/TreeUtils.ts +248 -0
- package/src/framework/utils/UrlUtils.ts +152 -0
- package/src/framework/utils/UuidUtils.ts +88 -0
- package/src/framework/utils/ValidateUtils.ts +28 -0
- package/src/framework/utils/index.ts +16 -0
- package/src/framework/utils/system/DictUtils.ts +97 -0
- package/src/framework/utils/system/FormRegistryUtils.ts +77 -0
- package/src/framework/utils/system/HttpUtils.ts +246 -0
- package/src/framework/utils/system/PageUtils.ts +153 -0
- package/src/framework/utils/system/PermUtils.ts +79 -0
- package/src/framework/utils/system/SysUtils.ts +97 -0
- package/src/framework/utils/system/ThemeUtils.ts +27 -0
- package/src/framework/utils/system/index.ts +7 -0
- package/src/framework/view-components/ViewBoolean.jsx +4 -0
- package/src/framework/view-components/index.js +3 -0
- package/src/layouts/PageRender.jsx +2 -2
- package/src/layouts/admin/HeaderRight.jsx +9 -9
- package/src/layouts/admin/TabPageRender.jsx +2 -2
- package/src/layouts/admin/index.jsx +27 -40
- package/src/layouts/admin/index.less +2 -1
- package/src/layouts/index.jsx +30 -38
- package/src/layouts/index.less +0 -21
- package/src/loading.jsx +2 -2
- package/src/{components → pages/flowable}/InstanceInfo.jsx +5 -4
- package/src/pages/flowable/design/customTranslate/customTranslate.js +16 -0
- package/src/pages/flowable/design/customTranslate/translations-properties-panel.js +10 -0
- package/src/pages/flowable/design/customTranslate/translations.js +144 -0
- package/src/pages/flowable/design/descriptors/flowable.json +1109 -0
- package/src/pages/flowable/design/index.jsx +61 -234
- package/src/pages/flowable/design/provider/FlowablePropertiesProvider.js +75 -0
- package/src/pages/flowable/design/provider/index.js +6 -0
- package/src/pages/flowable/design/provider/properties/ConditionDesign.jsx +175 -0
- package/src/pages/flowable/design/provider/properties/ConditionProps.jsx +76 -0
- package/src/pages/flowable/design/provider/properties/DelegateExpressionProps.js +54 -0
- package/src/pages/flowable/design/provider/properties/FormProps.js +55 -0
- package/src/pages/flowable/design/provider/properties/MultiInstanceProps.js +100 -0
- package/src/pages/flowable/design/provider/properties/UserTaskForm.jsx +48 -0
- package/src/pages/flowable/design/provider/properties/utils.jsx +35 -0
- package/src/pages/flowable/index.jsx +28 -69
- package/src/pages/flowable/monitor/definition.jsx +87 -0
- package/src/pages/flowable/monitor/instance/index.jsx +177 -0
- package/src/pages/flowable/monitor/instance/view.jsx +102 -0
- package/src/pages/flowable/monitor/task.jsx +93 -0
- package/src/pages/flowable/task/form.jsx +5 -5
- package/src/pages/flowable/task/index.jsx +5 -5
- package/src/pages/flowable/test/index.jsx +13 -10
- package/src/pages/index.jsx +8 -13
- package/src/pages/login.jsx +8 -8
- package/src/pages/{api/doc.jsx → system/api/ApiDoc.jsx} +56 -47
- package/src/pages/system/api/index.jsx +268 -0
- package/src/pages/system/api/perm.jsx +69 -0
- package/src/pages/system/config/index.jsx +3 -3
- package/src/pages/system/dict/Dict.jsx +2 -3
- package/src/pages/system/dict/DictItem.jsx +5 -5
- package/src/pages/system/file/index.jsx +13 -5
- package/src/pages/{job → system/job}/index.jsx +124 -39
- package/src/pages/system/log/index.jsx +3 -3
- package/src/pages/system/org/index.jsx +11 -12
- package/src/pages/system/role/index.jsx +11 -11
- package/src/pages/system/role/perm.jsx +8 -8
- package/src/pages/system/sysManual/index.jsx +6 -6
- package/src/pages/system/user/UserPerm.jsx +7 -42
- package/src/pages/system/user/index.jsx +13 -17
- package/src/pages/test.jsx +188 -3
- package/src/pages/ureport/index.jsx +4 -4
- package/src/pages/userCenter/ChangePassword.jsx +3 -4
- package/src/pages/userCenter/index.jsx +2 -2
- package/src/pages/userCenter/manual.jsx +3 -7
- package/src/pages/userCenter/message.jsx +5 -5
- package/config/dist/config.local.js +0 -23
- package/src/components/InstanceStatusInfo.jsx +0 -79
- package/src/components/StreamLog.jsx +0 -27
- package/src/components/flow/BpmnUtils.js +0 -85
- package/src/components/flow/customTranslate/customTranslate.js +0 -19
- package/src/components/flow/customTranslate/translations.js +0 -79
- package/src/components/flow/design/form/ConditionForm.jsx +0 -316
- package/src/components/flow/design/form/ServiceTaskForm.jsx +0 -55
- package/src/components/flow/design/form/TimerEventDefinitionForm.jsx +0 -62
- package/src/components/flow/design/form/UserTaskForm.jsx +0 -211
- package/src/components/flow/design/palette.js +0 -39
- package/src/components/monitor/AllDefinition.jsx +0 -46
- package/src/components/monitor/AllInstance.jsx +0 -76
- package/src/forms/driverForm.jsx +0 -18
- package/src/framework/components/ButtonList.jsx +0 -91
- package/src/framework/components/Echarts/index.d.ts +0 -10
- package/src/framework/components/Echarts/index.jsx +0 -49
- package/src/framework/components/EditTable/index.d.ts +0 -11
- package/src/framework/components/EditTable/index.jsx +0 -85
- package/src/framework/components/FieldRemoteTree.jsx +0 -20
- package/src/framework/components/HasPerm.jsx +0 -18
- package/src/framework/components/ImageView.d.ts +0 -12
- package/src/framework/components/ImageView.jsx +0 -64
- package/src/framework/components/MsgBox.jsx +0 -180
- package/src/framework/components/PageLoading.jsx +0 -31
- package/src/framework/components/Panel/index.d.ts +0 -11
- package/src/framework/components/Panel/index.jsx +0 -24
- package/src/framework/components/Panel/index.less +0 -0
- package/src/framework/components/field/FieldAutoTime/index.tsx +0 -46
- package/src/framework/components/field/FieldBoolean/index.tsx +0 -92
- package/src/framework/components/field/FieldEditTable/index.less +0 -29
- package/src/framework/components/field/FieldEditor.jsx +0 -48
- package/src/framework/components/field/FieldFileBase64/index.d.ts +0 -15
- package/src/framework/components/field/FieldFileBase64/index.jsx +0 -23
- package/src/framework/components/field/FieldImageBase64/index.d.ts +0 -15
- package/src/framework/components/field/FieldImageBase64/index.jsx +0 -22
- package/src/framework/components/field/FieldInput.jsx +0 -13
- package/src/framework/components/field/FieldOrgTree/index.tsx +0 -45
- package/src/framework/components/field/FieldPassword.jsx +0 -11
- package/src/framework/components/field/FieldProps.ts +0 -19
- package/src/framework/components/field/FieldRemoteTreeCascader.jsx +0 -75
- package/src/framework/components/field/FieldRemoteTreeCheckable.jsx +0 -81
- package/src/framework/components/field/FieldRemoteTreeMultipleSelect.jsx +0 -100
- package/src/framework/components/field/FieldRemoteTreeSelect.jsx +0 -82
- package/src/framework/components/field/FieldSelectPosition/index.tsx +0 -85
- package/src/framework/components/field/FieldTree.jsx +0 -45
- package/src/framework/components/field/dict/index.tsx +0 -110
- package/src/framework/components/field/flat-multiple-select/index.d.ts +0 -4
- package/src/framework/components/field/flat-multiple-select/index.jsx +0 -116
- package/src/framework/components/field/flat-multiple-select/index.less +0 -12
- package/src/framework/components/field/flat-select/index.d.ts +0 -4
- package/src/framework/components/field/flat-select/index.jsx +0 -107
- package/src/framework/components/field/flat-select/index.less +0 -12
- package/src/framework/components/field/index.js +0 -33
- package/src/framework/components/field/input-number-percent/index.d.ts +0 -11
- package/src/framework/components/field/input-number-percent/index.jsx +0 -28
- package/src/framework/components/field/select/FieldSelect/index.d.ts +0 -39
- package/src/framework/components/field/select/FieldSelect/index.jsx +0 -156
- package/src/framework/components/field/select/FieldTableSelect/index.jsx +0 -155
- package/src/framework/components/field/select/index.jsx +0 -2
- package/src/framework/components/field/switch-y-n/index.d.ts +0 -11
- package/src/framework/components/field/switch-y-n/index.jsx +0 -31
- package/src/framework/components/field/text/index.tsx +0 -7
- package/src/framework/components/field/time/DatePickerString.tsx +0 -37
- package/src/framework/components/field/time/DateTimePickerString.tsx +0 -42
- package/src/framework/components/field/time/FieldDateRange.d.ts +0 -9
- package/src/framework/components/field/time/FieldDateRange.jsx +0 -49
- package/src/framework/components/field/time/HHmmPickerString.tsx +0 -42
- package/src/framework/components/field/time/MonthPickerString.tsx +0 -45
- package/src/framework/components/field/time/TimePickerTool.ts +0 -14
- package/src/framework/components/field/time/YearPickerString.tsx +0 -41
- package/src/framework/components/field/time/YearQuarterString.tsx +0 -38
- package/src/framework/components/field/time/index.tsx +0 -7
- package/src/framework/components/field/upload/FieldUploadCropImage/index.d.ts +0 -22
- package/src/framework/components/field/upload/FieldUploadCropImage/index.jsx +0 -11
- package/src/framework/components/field/upload/FieldUploadImage.d.ts +0 -7
- package/src/framework/components/field/upload/FieldUploadImage.jsx +0 -16
- package/src/framework/components/field/upload/index.jsx +0 -3
- package/src/framework/components/system.jsx +0 -29
- package/src/framework/components/view/ViewBoolean.jsx +0 -15
- package/src/framework/components/view/ViewBooleanEnableDisable.jsx +0 -16
- package/src/framework/components/view/ViewText.jsx +0 -9
- package/src/framework/index.js +0 -3
- package/src/framework/system/dict.js +0 -88
- package/src/framework/system/formRegistry.js +0 -58
- package/src/framework/system/http.jsx +0 -290
- package/src/framework/system/index.js +0 -13
- package/src/framework/system/page.js +0 -109
- package/src/framework/system/permission.js +0 -44
- package/src/framework/system/sys.js +0 -60
- package/src/framework/system/theme.js +0 -17
- package/src/framework/utils/arr.js +0 -118
- package/src/framework/utils/browser.js +0 -24
- package/src/framework/utils/color.js +0 -269
- package/src/framework/utils/debounce.js +0 -33
- package/src/framework/utils/dom.js +0 -16
- package/src/framework/utils/eventBus.js +0 -70
- package/src/framework/utils/html.js +0 -13
- package/src/framework/utils/index.js +0 -17
- package/src/framework/utils/obj.js +0 -72
- package/src/framework/utils/storage.js +0 -37
- package/src/framework/utils/str.js +0 -297
- package/src/framework/utils/tree.js +0 -175
- package/src/framework/utils/uid.js +0 -76
- package/src/framework/utils/url.js +0 -84
- package/src/framework/utils/validate.js +0 -9
- package/src/pages/api/accessLog/index.jsx +0 -108
- package/src/pages/api/accountResource/index.jsx +0 -149
- package/src/pages/api/index.jsx +0 -163
- package/src/pages/api/resource.jsx +0 -18
- package/src/pages/flowable/instance/view.jsx +0 -13
- package/src/pages/flowable/monitor.jsx +0 -106
- package/src/pages/flowable/test/form.jsx +0 -31
- package/src/pages/job/logList.jsx +0 -100
- package/src/pages/job/status.jsx +0 -84
- /package/src/framework/components/{ButtonList.d.ts → system/ButtonList.d.ts} +0 -0
- /package/src/framework/{components/EditTable/index.less → field-components/FieldTable.less} +0 -0
- /package/src/framework/{components/view → view-components}/ViewPassword.jsx +0 -0
- /package/src/{index.js → index.ts} +0 -0
- /package/src/{components/flow → pages/flowable}/design/contextPad.js +0 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 字符串工具类
|
|
3
|
+
*/
|
|
4
|
+
export class StringUtils {
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 移除字符串前缀
|
|
8
|
+
* @param str 原始字符串
|
|
9
|
+
* @param ch 要移除的前缀
|
|
10
|
+
* @returns 移除前缀后的字符串,或原字符串
|
|
11
|
+
*/
|
|
12
|
+
static removePrefix(str: string | null | undefined, ch: string): string | null | undefined {
|
|
13
|
+
if (str != null && str.startsWith(ch)) {
|
|
14
|
+
return str.substring(ch.length);
|
|
15
|
+
}
|
|
16
|
+
return str;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 移除字符串后缀
|
|
21
|
+
* @param str 原始字符串
|
|
22
|
+
* @param ch 要移除的后缀
|
|
23
|
+
* @returns 移除后缀后的字符串,或原字符串
|
|
24
|
+
*/
|
|
25
|
+
static removeSuffix(str: string | null | undefined, ch: string): string | null | undefined {
|
|
26
|
+
if (str != null && str.endsWith(ch)) {
|
|
27
|
+
return str.substring(0, str.length - ch.length);
|
|
28
|
+
}
|
|
29
|
+
return str;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 生成指定长度的随机字符串
|
|
34
|
+
* @param length 随机字符串的长度
|
|
35
|
+
* @returns 生成的随机字符串
|
|
36
|
+
*/
|
|
37
|
+
static random(length: number): string {
|
|
38
|
+
const characters: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
39
|
+
let result: string = '';
|
|
40
|
+
for (let i = 0; i < length; i++) {
|
|
41
|
+
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 处理空值,返回 "key未定义"
|
|
48
|
+
* @param key 键名
|
|
49
|
+
* @returns 处理后的字符串
|
|
50
|
+
*/
|
|
51
|
+
static nullText(key: string | null | undefined): string {
|
|
52
|
+
return (key || '') + '未定义';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 检查字符串是否包含子字符串
|
|
57
|
+
* @param str 原始字符串
|
|
58
|
+
* @param subStr 要检查的子字符串
|
|
59
|
+
* @returns 如果包含则返回 true,否则返回 false
|
|
60
|
+
*/
|
|
61
|
+
static contains(str: string | null | undefined, subStr: string): boolean {
|
|
62
|
+
if (!str) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return str.includes(subStr);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 统计子字符串在原始字符串中出现的次数
|
|
70
|
+
* @param str 原始字符串
|
|
71
|
+
* @param subStr 要统计的子字符串
|
|
72
|
+
* @returns 出现的次数
|
|
73
|
+
*/
|
|
74
|
+
static count(str: string | null | undefined, subStr: string): number {
|
|
75
|
+
if (str == null || str.length === 0) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
let count: number = 0;
|
|
79
|
+
let index: number = 0;
|
|
80
|
+
|
|
81
|
+
while (true) {
|
|
82
|
+
index = str.indexOf(subStr, index);
|
|
83
|
+
if (index === -1) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
count++;
|
|
87
|
+
index += subStr.length;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return count;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 将字符串的首字母转换为大写
|
|
95
|
+
* @param str 原始字符串
|
|
96
|
+
* @returns 首字母大写后的字符串,或原值
|
|
97
|
+
*/
|
|
98
|
+
static capitalize(str: string | null | undefined): string | null | undefined {
|
|
99
|
+
if (str == null) {
|
|
100
|
+
return str;
|
|
101
|
+
}
|
|
102
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 颠倒字符串的顺序
|
|
107
|
+
* @param str 原始字符串
|
|
108
|
+
* @returns 颠倒顺序后的字符串,或原值
|
|
109
|
+
*/
|
|
110
|
+
static reverse(str: string | null | undefined): string | null | undefined {
|
|
111
|
+
if (str == null) {
|
|
112
|
+
return str;
|
|
113
|
+
}
|
|
114
|
+
return str.split('').reverse().join('');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 截取字符串,返回子字符串后面部分。如果不包含,则原样返回
|
|
119
|
+
* @param source 原始字符串
|
|
120
|
+
* @param str 分隔符
|
|
121
|
+
* @returns 分隔符后面的字符串,或原字符串
|
|
122
|
+
*/
|
|
123
|
+
static subAfter(source: string | null | undefined, str: string): string | null | undefined {
|
|
124
|
+
if (source == null) {
|
|
125
|
+
return source;
|
|
126
|
+
}
|
|
127
|
+
const index = source.indexOf(str);
|
|
128
|
+
return index === -1 ? source : source.substring(index + str.length); // 修正:应为 index + str.length
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 截取字符串,返回最后一个子字符串后面的部分。如果不包含,则原样返回
|
|
133
|
+
* @param source 原始字符串
|
|
134
|
+
* @param str 分隔符
|
|
135
|
+
* @returns 最后一个分隔符后面的字符串,或原字符串
|
|
136
|
+
*/
|
|
137
|
+
static subAfterLast(source: string | null | undefined, str: string): string | null | undefined {
|
|
138
|
+
if (source == null) {
|
|
139
|
+
return source;
|
|
140
|
+
}
|
|
141
|
+
const index = source.lastIndexOf(str);
|
|
142
|
+
return index === -1 ? source : source.substring(index + str.length); // 修正:应为 index + str.length
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 截取字符串,返回子字符串前面的部分。如果不包含,则原样返回
|
|
147
|
+
* @param s 原始字符串
|
|
148
|
+
* @param sub 分隔符
|
|
149
|
+
* @returns 分隔符前面的字符串,或原字符串
|
|
150
|
+
*/
|
|
151
|
+
static subBefore(s: string | null | undefined, sub: string): string | null | undefined {
|
|
152
|
+
if (s == null) {
|
|
153
|
+
return s;
|
|
154
|
+
}
|
|
155
|
+
const index = s.indexOf(sub);
|
|
156
|
+
return index === -1 ? s : s.substring(0, index);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 混淆字符串 (通过在每个字符后添加 '1')
|
|
161
|
+
* @param str 原始字符串
|
|
162
|
+
* @returns 混淆后的字符串
|
|
163
|
+
*/
|
|
164
|
+
static obfuscateString(str: string | null | undefined): string | null | undefined {
|
|
165
|
+
if (str == null) {
|
|
166
|
+
return str;
|
|
167
|
+
}
|
|
168
|
+
return str.split('').map(char => char + '1').join('');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 补零或补指定字符
|
|
173
|
+
* @param input 输入值(字符串或数字)
|
|
174
|
+
* @param totalLen 总长度
|
|
175
|
+
* @param padChar 填充字符,默认为 '0'
|
|
176
|
+
* @returns 填充后的字符串
|
|
177
|
+
*/
|
|
178
|
+
static pad(input: string | number | null | undefined, totalLen: number, padChar: string = '0'): string {
|
|
179
|
+
if (input == null) {
|
|
180
|
+
return padChar.repeat(totalLen);
|
|
181
|
+
}
|
|
182
|
+
let str = String(input);
|
|
183
|
+
const charsNeeded: number = totalLen - str.length;
|
|
184
|
+
if (charsNeeded > 0) {
|
|
185
|
+
str = padChar.repeat(charsNeeded) + str;
|
|
186
|
+
}
|
|
187
|
+
return str;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 简单加密:将每个字符的 ASCII 码加 1,并转换为四位十六进制表示
|
|
192
|
+
* @param str 原始字符串
|
|
193
|
+
* @returns 加密后的十六进制字符串
|
|
194
|
+
*/
|
|
195
|
+
static encrypt(str: string | null | undefined): string | null | undefined {
|
|
196
|
+
if (str == null) {
|
|
197
|
+
return str;
|
|
198
|
+
}
|
|
199
|
+
let encrypted: string = '';
|
|
200
|
+
for (let i = 0; i < str.length; i++) {
|
|
201
|
+
let charCode: number = str.charCodeAt(i);
|
|
202
|
+
charCode += 1;
|
|
203
|
+
// 转换为四位的十六进制表示,并确保至少四位
|
|
204
|
+
encrypted += ('0000' + charCode.toString(16)).slice(-4);
|
|
205
|
+
}
|
|
206
|
+
return encrypted;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 简单解密:还原加密字符串
|
|
211
|
+
* @param hexString 加密后的十六进制字符串
|
|
212
|
+
* @returns 解密后的字符串
|
|
213
|
+
*/
|
|
214
|
+
static decrypt(hexString: string | null | undefined): string | null | undefined {
|
|
215
|
+
if (hexString == null) {
|
|
216
|
+
return hexString;
|
|
217
|
+
}
|
|
218
|
+
let decrypted: string = '';
|
|
219
|
+
// 确保是 4 的倍数,否则可能无法正确解密
|
|
220
|
+
if (hexString.length % 4 !== 0) {
|
|
221
|
+
// 应该抛出错误或根据业务逻辑处理,这里为兼容原 js 逻辑,简单返回
|
|
222
|
+
return hexString;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (let i = 0; i < hexString.length; i += 4) {
|
|
226
|
+
const hexCharCode: string = hexString.substring(i, i + 4);
|
|
227
|
+
let charCode: number = parseInt(hexCharCode, 16); // 将十六进制转换为十进制
|
|
228
|
+
if (isNaN(charCode)) {
|
|
229
|
+
// 如果解析失败,也应根据业务逻辑处理
|
|
230
|
+
return hexString;
|
|
231
|
+
}
|
|
232
|
+
charCode -= 1;
|
|
233
|
+
decrypted += String.fromCharCode(charCode);
|
|
234
|
+
}
|
|
235
|
+
return decrypted;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 获取字符串的显示宽度:英文字符 1 宽,中文字符 2 宽
|
|
240
|
+
* @param str 原始字符串
|
|
241
|
+
* @returns 字符串的显示宽度
|
|
242
|
+
*/
|
|
243
|
+
static getWidth(str: string | null | undefined): number {
|
|
244
|
+
if (str == null || str.length === 0) {
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
// 使用非空断言 ! 简化
|
|
248
|
+
return str!.split('').reduce((pre, cur) => {
|
|
249
|
+
const charCode: number = cur.charCodeAt(0);
|
|
250
|
+
// 假设 0-128 为半角字符
|
|
251
|
+
if (charCode >= 0 && charCode <= 128) {
|
|
252
|
+
return pre + 1;
|
|
253
|
+
}
|
|
254
|
+
// 其它视为全角字符
|
|
255
|
+
return pre + 2;
|
|
256
|
+
}, 0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 按显示宽度截取字符串
|
|
261
|
+
* @param str 原始字符串
|
|
262
|
+
* @param maxWidth 最大显示宽度
|
|
263
|
+
* @returns 截取后的字符串
|
|
264
|
+
*/
|
|
265
|
+
static cutByWidth(str: string, maxWidth: number): string {
|
|
266
|
+
let showLength: number = 0;
|
|
267
|
+
return str.split('').reduce((pre, cur) => {
|
|
268
|
+
const charCode: number = cur.charCodeAt(0);
|
|
269
|
+
let charWidth: number = (charCode >= 0 && charCode <= 128) ? 1 : 2;
|
|
270
|
+
|
|
271
|
+
if (showLength + charWidth <= maxWidth) {
|
|
272
|
+
showLength += charWidth;
|
|
273
|
+
return pre + cur;
|
|
274
|
+
}
|
|
275
|
+
return pre;
|
|
276
|
+
}, '');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* 字符串省略处理(按显示宽度计算)
|
|
282
|
+
* @param str 原始字符串
|
|
283
|
+
* @param len 字符长度(宽度),注:中文字符算 2
|
|
284
|
+
* @param suffix 省略号后缀,默认为 '...'
|
|
285
|
+
* @returns 处理后的字符串
|
|
286
|
+
*/
|
|
287
|
+
static ellipsis(str: string | null | undefined, len: number, suffix: string = '...'): string | null | undefined {
|
|
288
|
+
if (str == null) {
|
|
289
|
+
return str;
|
|
290
|
+
}
|
|
291
|
+
if (!StringUtils.isStr(str)) { // 使用类方法调用
|
|
292
|
+
return str;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 快速判断
|
|
296
|
+
if (str.length * 2 < len) {
|
|
297
|
+
return str;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const fullLength: number = StringUtils.getWidth(str);
|
|
301
|
+
|
|
302
|
+
if (fullLength <= len) {
|
|
303
|
+
return str;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 截取
|
|
307
|
+
return StringUtils.cutByWidth(str, len) + suffix;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 判断值是否为字符串类型
|
|
312
|
+
* @param value 任意值
|
|
313
|
+
* @returns 是否为字符串
|
|
314
|
+
*/
|
|
315
|
+
static isStr(value: any): value is string {
|
|
316
|
+
return typeof value === 'string';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 将下划线或连字符分隔的字符串转为驼峰命名
|
|
322
|
+
* @param str 原始字符串
|
|
323
|
+
* @param firstLower 转换后首字母是否小写,默认为 true
|
|
324
|
+
* @returns 转换后的驼峰字符串
|
|
325
|
+
*/
|
|
326
|
+
static toCamelCase(str: string, firstLower: boolean = true): string {
|
|
327
|
+
// 匹配下划线或连字符后跟一个字母
|
|
328
|
+
let result: string = str.replace(/[-_](\w)/g, function (all, letter) {
|
|
329
|
+
return letter.toUpperCase();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (firstLower) {
|
|
333
|
+
// 确保首字母小写
|
|
334
|
+
result = result.substring(0, 1).toLowerCase() + result.substring(1);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* 将驼峰命名字符串转为下划线命名
|
|
342
|
+
* @param name 驼峰命名字符串
|
|
343
|
+
* @returns 下划线命名字符串
|
|
344
|
+
*/
|
|
345
|
+
static toUnderlineCase(name: string | null | undefined): string | null | undefined {
|
|
346
|
+
if (name == null) {
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
// 在大写字母前添加下划线,并全部转小写
|
|
350
|
+
let result: string = name.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
351
|
+
|
|
352
|
+
// 移除开头多余的下划线
|
|
353
|
+
if (result.startsWith('_')) {
|
|
354
|
+
result = result.substring(1);
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* 比较两个字符串是否相等,忽略大小写
|
|
361
|
+
* @param a 字符串 a
|
|
362
|
+
* @param b 字符串 b
|
|
363
|
+
* @returns 是否相等(忽略大小写)
|
|
364
|
+
*/
|
|
365
|
+
static equalsIgnoreCase(a: string | null | undefined, b: string | null | undefined): boolean {
|
|
366
|
+
if (a === b) { // 引用相同,或同时为 null/undefined
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (a != null && b != null) {
|
|
371
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// 只有在 a === b 时,null/undefined 才是 true
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 分割字符串
|
|
380
|
+
* @param str
|
|
381
|
+
* @param sp
|
|
382
|
+
*/
|
|
383
|
+
static split(str: any, sp: string):null|string[] {
|
|
384
|
+
if(str == null || str.length === 0){
|
|
385
|
+
return undefined
|
|
386
|
+
}
|
|
387
|
+
if(Array.isArray(str)){
|
|
388
|
+
return str;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return str.split(sp)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* 连接字符串
|
|
396
|
+
* @param arr
|
|
397
|
+
* @param sp 分隔符
|
|
398
|
+
*/
|
|
399
|
+
static join(arr, sp: string) {
|
|
400
|
+
if (arr == null) {
|
|
401
|
+
return []
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if(!Array.isArray(arr)){
|
|
405
|
+
return arr;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return arr.join(sp);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {ArrUtils} from "./ArrUtils"; // 假设 ArrUtil 已经被正确定义和导出
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 🌳 通用树节点接口
|
|
5
|
+
* 假设默认的 id, pid, children 字段存在,但支持其他字段
|
|
6
|
+
* @template T 节点数据的类型
|
|
7
|
+
*/
|
|
8
|
+
export interface TreeNode<T = any> {
|
|
9
|
+
/** 节点的唯一标识 */
|
|
10
|
+
id: string | number;
|
|
11
|
+
/** 父节点的标识 */
|
|
12
|
+
pid: string | number | null;
|
|
13
|
+
/** 子节点数组 */
|
|
14
|
+
children?: TreeNode<T>[];
|
|
15
|
+
|
|
16
|
+
/** 节点携带的额外数据 */
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 🌲 树结构操作工具类
|
|
22
|
+
* 将数组和树结构互相转换,并提供常用的遍历、查找功能。
|
|
23
|
+
*/
|
|
24
|
+
export class TreeUtils {
|
|
25
|
+
|
|
26
|
+
// --- 内部辅助函数 ---
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 递归将树转换为列表的实现。
|
|
30
|
+
* @param tree 树节点数组
|
|
31
|
+
* @param level 当前层级
|
|
32
|
+
* @param result 结果列表,用于累积结果
|
|
33
|
+
* @returns 包含层级信息的列表
|
|
34
|
+
*/
|
|
35
|
+
private static _treeToList<T extends TreeNode>(tree: T[], level: number, result: (T & { level: number })[]): (T & {
|
|
36
|
+
level: number
|
|
37
|
+
})[] {
|
|
38
|
+
for (const node of tree) {
|
|
39
|
+
// 复制节点并添加 level 字段
|
|
40
|
+
const newNode = {...node, level: level} as T & { level: number };
|
|
41
|
+
result.push(newNode);
|
|
42
|
+
|
|
43
|
+
if (node.children && node.children.length > 0) {
|
|
44
|
+
// 递归调用,层级加 1
|
|
45
|
+
this._treeToList(node.children as T[], level + 1, result);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// --- 核心工具方法 ---
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 🌲 将树结构转换为列表结构 (扁平化)。
|
|
55
|
+
* 节点会被添加一个 `level` 字段表示其层级 (从 1 开始)。
|
|
56
|
+
* @param tree 树节点数组
|
|
57
|
+
* @returns 包含层级信息的扁平化列表
|
|
58
|
+
*/
|
|
59
|
+
public static treeToList<T extends TreeNode>(tree: T[]): (T & { level: number }) {
|
|
60
|
+
return TreeUtils._treeToList(tree, 1, []);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 🔎 根据层级查找所有节点的 ID 列表。
|
|
65
|
+
* @param tree 树节点数组
|
|
66
|
+
* @param level 要查找的层级,-1 表示所有层级的 ID。
|
|
67
|
+
* @returns 匹配层级的 ID 数组。
|
|
68
|
+
*/
|
|
69
|
+
public static findKeysByLevel<T extends TreeNode>(tree: T[], level: number): (string | number)[] {
|
|
70
|
+
const list = TreeUtils.treeToList(tree);
|
|
71
|
+
// 如果 level 为 -1,则返回所有节点的 id;否则返回匹配 level 的节点 id
|
|
72
|
+
return list
|
|
73
|
+
.filter(t => level === -1 || t.level === level)
|
|
74
|
+
.map(t => t.id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 🏗️ 将扁平数组转换为树结构。
|
|
79
|
+
* 默认使用 `id` 作为节点标识,`pid` 作为父节点标识。
|
|
80
|
+
* @param list 扁平化节点数组
|
|
81
|
+
* @param idKey 节点 ID 的字段名,默认为 'id'
|
|
82
|
+
* @param pidKey 父节点 ID 的字段名,默认为 'pid'
|
|
83
|
+
* @returns 根节点数组
|
|
84
|
+
*/
|
|
85
|
+
public static buildTree<T extends TreeNode>(list: T[], idKey: keyof T | 'id' = 'id', pidKey: keyof T | 'pid' = 'pid'): T[] {
|
|
86
|
+
// 使用 Map 存储所有节点,方便通过 ID 查找
|
|
87
|
+
const map = new Map<string | number, T>();
|
|
88
|
+
list.forEach(node => {
|
|
89
|
+
const id = node[idKey] as string | number;
|
|
90
|
+
map.set(id, node);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const root: T[] = [];
|
|
94
|
+
for (const node of list) {
|
|
95
|
+
const pid = node[pidKey] as string | number | null | undefined;
|
|
96
|
+
// 通过 pidKey 查找父节点
|
|
97
|
+
const parent = pid ? map.get(pid) : undefined;
|
|
98
|
+
|
|
99
|
+
if (parent) {
|
|
100
|
+
// 如果找到父节点,将当前节点挂载到父节点的 children 数组
|
|
101
|
+
if (!parent.children) {
|
|
102
|
+
parent.children = [];
|
|
103
|
+
}
|
|
104
|
+
// TypeScript 确保 children 数组的类型正确
|
|
105
|
+
(parent.children as T[]).push(node);
|
|
106
|
+
} else {
|
|
107
|
+
// 如果没有父节点 (或找不到),则视为根节点
|
|
108
|
+
root.push(node);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return root;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 🚶 深度优先遍历树节点。
|
|
117
|
+
* @param tree 树节点数组
|
|
118
|
+
* @param callback 对每个节点执行的回调函数
|
|
119
|
+
*/
|
|
120
|
+
public static walk<T extends TreeNode>(tree: T[], callback: (node: T) => void): void {
|
|
121
|
+
for (const node of tree) {
|
|
122
|
+
callback(node); // 执行回调函数
|
|
123
|
+
|
|
124
|
+
// 遍历子节点
|
|
125
|
+
if (node.children && node.children.length > 0) {
|
|
126
|
+
TreeUtils.walk(node.children as T[], callback);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 🔑 根据键值深度查找单个节点。
|
|
133
|
+
* @param key 要查找的键值 (例如: 节点的 id)
|
|
134
|
+
* @param list 树节点数组
|
|
135
|
+
* @param keyName 要匹配的字段名,默认为 'id'
|
|
136
|
+
* @returns 找到的节点,如果未找到则返回 undefined
|
|
137
|
+
*/
|
|
138
|
+
public static findByKey<T extends TreeNode>(key: string | number, list: T[], keyName: keyof T | 'id' = 'id'): T | undefined {
|
|
139
|
+
for (const item of list) {
|
|
140
|
+
// 匹配当前节点
|
|
141
|
+
if (item[keyName] === key) {
|
|
142
|
+
return item;
|
|
143
|
+
}
|
|
144
|
+
// 递归查找子节点
|
|
145
|
+
if (item.children && item.children.length) {
|
|
146
|
+
const rs = TreeUtils.findByKey(key, item.children as T[], keyName);
|
|
147
|
+
if (rs) {
|
|
148
|
+
return rs;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 📜 根据键值列表查找所有匹配的节点列表。
|
|
158
|
+
* @param treeData 树节点数组
|
|
159
|
+
* @param keyList 要查找的键值列表
|
|
160
|
+
* @returns 匹配的节点数组
|
|
161
|
+
*/
|
|
162
|
+
public static findByKeyList<T extends TreeNode>(treeData: T[], keyList: (string | number)[]): T[] {
|
|
163
|
+
const itemList: T[] = [];
|
|
164
|
+
|
|
165
|
+
TreeUtils.walk(treeData, (item) => {
|
|
166
|
+
// 优先使用 key 字段,其次使用 id 字段
|
|
167
|
+
const key = item.key || item.id;
|
|
168
|
+
|
|
169
|
+
if (ArrUtils.contains(keyList, key)) {
|
|
170
|
+
itemList.push(item);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return itemList;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 获得指定节点下的所有子节点 (扁平化列表,包含自身)。
|
|
179
|
+
* @param treeNode 树节点
|
|
180
|
+
* @param buffer 结果列表,用于递归累积
|
|
181
|
+
* @returns 包含所有子节点的扁平化列表 (包含自身)
|
|
182
|
+
*/
|
|
183
|
+
private static _getChild<T extends TreeNode>(treeNode: T, buffer: T[]): T[] {
|
|
184
|
+
if (treeNode.children != null && treeNode.children.length > 0) {
|
|
185
|
+
treeNode.children.forEach((c) => {
|
|
186
|
+
buffer.push(c as T);
|
|
187
|
+
TreeUtils._getChild(c as T, buffer);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return buffer;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 获得给定根节点列表下的所有节点 (扁平化列表)。
|
|
195
|
+
* @param treeNodeList 树节点数组
|
|
196
|
+
* @returns 包含所有节点的扁平化列表
|
|
197
|
+
*/
|
|
198
|
+
public static getSimpleList<T extends TreeNode>(treeNodeList: T[]): T[] {
|
|
199
|
+
const buffer: T[] = [];
|
|
200
|
+
|
|
201
|
+
if (treeNodeList != null) {
|
|
202
|
+
treeNodeList.forEach((t) => {
|
|
203
|
+
buffer.push(t);
|
|
204
|
+
TreeUtils._getChild(t, buffer);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return buffer;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 向上追溯,获取从根节点到指定值节点的完整 Key 路径。
|
|
213
|
+
* 假设节点包含 `id` / `key` 和 `pid` / `parentKey` 字段。
|
|
214
|
+
* @param tree 完整的树结构数组
|
|
215
|
+
* @param value 目标节点的 ID 或 Key
|
|
216
|
+
* @returns 从根节点开始到目标节点的 ID/Key 数组 (包含根和目标节点)
|
|
217
|
+
*/
|
|
218
|
+
public static getKeyList<T extends TreeNode>(tree: T[], value: string | number): (string | number)[] {
|
|
219
|
+
const list = TreeUtils.getSimpleList(tree);
|
|
220
|
+
|
|
221
|
+
// 使用 Map 快速通过 ID/Key 查找节点
|
|
222
|
+
const map = new Map<string | number, T>();
|
|
223
|
+
list.forEach((t) => {
|
|
224
|
+
const key = t.key || t.id;
|
|
225
|
+
map.set(key, t);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const targetNode = map.get(value);
|
|
229
|
+
if (targetNode == null) {
|
|
230
|
+
return []; // 未找到目标节点
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const keys: (string | number)[] = [targetNode.key || targetNode.id];
|
|
234
|
+
let parentKey = targetNode.parentKey || targetNode.pid;
|
|
235
|
+
|
|
236
|
+
// 向上追溯
|
|
237
|
+
while (parentKey != null) {
|
|
238
|
+
const parent = map.get(parentKey);
|
|
239
|
+
if (parent == null) {
|
|
240
|
+
break; // 找不到父节点,停止追溯
|
|
241
|
+
}
|
|
242
|
+
keys.push(parent.key || parent.id);
|
|
243
|
+
parentKey = parent.parentKey || parent.pid;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return keys.reverse(); // 从根节点到目标节点的路径
|
|
247
|
+
}
|
|
248
|
+
}
|