@jiangood/open-admin 1.0.0-beta.5

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.
Files changed (186) hide show
  1. package/config/common-plugin.js +94 -0
  2. package/config/config.js +58 -0
  3. package/config/utils.js +73 -0
  4. package/package.json +42 -0
  5. package/src/.umi-production/appData.json +2365 -0
  6. package/src/.umi-production/core/EmptyRoute.tsx +9 -0
  7. package/src/.umi-production/core/defineApp.ts +16 -0
  8. package/src/.umi-production/core/helmet.ts +10 -0
  9. package/src/.umi-production/core/helmetContext.ts +4 -0
  10. package/src/.umi-production/core/history.ts +72 -0
  11. package/src/.umi-production/core/historyIntelli.ts +132 -0
  12. package/src/.umi-production/core/plugin.ts +40 -0
  13. package/src/.umi-production/core/pluginConfig.ts +324 -0
  14. package/src/.umi-production/core/pluginConfigJoi.d.ts +7 -0
  15. package/src/.umi-production/core/polyfill.ts +220 -0
  16. package/src/.umi-production/core/route.tsx +54 -0
  17. package/src/.umi-production/core/routeProps.js +5 -0
  18. package/src/.umi-production/core/routeProps.ts +6 -0
  19. package/src/.umi-production/core/terminal.ts +37 -0
  20. package/src/.umi-production/exports.ts +17 -0
  21. package/src/.umi-production/testBrowser.tsx +90 -0
  22. package/src/.umi-production/tsconfig.json +44 -0
  23. package/src/.umi-production/typings.d.ts +136 -0
  24. package/src/.umi-production/umi.ts +83 -0
  25. package/src/framework/components/DownloadFileButton/index.d.ts +11 -0
  26. package/src/framework/components/DownloadFileButton/index.jsx +33 -0
  27. package/src/framework/components/Gap/index.d.ts +23 -0
  28. package/src/framework/components/Gap/index.jsx +46 -0
  29. package/src/framework/components/LinkButton/index.d.ts +14 -0
  30. package/src/framework/components/LinkButton/index.jsx +10 -0
  31. package/src/framework/components/NamedIcon/index.d.ts +5 -0
  32. package/src/framework/components/NamedIcon/index.jsx +11 -0
  33. package/src/framework/components/OrgTree/index.d.ts +4 -0
  34. package/src/framework/components/OrgTree/index.jsx +58 -0
  35. package/src/framework/components/Page/index.d.ts +17 -0
  36. package/src/framework/components/Page/index.jsx +30 -0
  37. package/src/framework/components/Page/index.less +10 -0
  38. package/src/framework/components/PageLoading/index.d.ts +1 -0
  39. package/src/framework/components/PageLoading/index.jsx +25 -0
  40. package/src/framework/components/ProModal/index.tsx +66 -0
  41. package/src/framework/components/ProTable/components/ToolBar/index.jsx +123 -0
  42. package/src/framework/components/ProTable/components/ToolBar/index.less +53 -0
  43. package/src/framework/components/ProTable/index.d.ts +42 -0
  44. package/src/framework/components/ProTable/index.jsx +260 -0
  45. package/src/framework/components/ProTable/index.less +14 -0
  46. package/src/framework/components/ProTable/utils/index.js +43 -0
  47. package/src/framework/components/RoleTree/index.d.ts +4 -0
  48. package/src/framework/components/RoleTree/index.jsx +50 -0
  49. package/src/framework/components/ValueType/index.jsx +34 -0
  50. package/src/framework/components/ValueType/registry.jsx +26 -0
  51. package/src/framework/components/ViewRange/index.d.ts +14 -0
  52. package/src/framework/components/ViewRange/index.jsx +20 -0
  53. package/src/framework/components/index.ts +13 -0
  54. package/src/framework/components/system/ButtonList.d.ts +8 -0
  55. package/src/framework/components/system/ButtonList.jsx +42 -0
  56. package/src/framework/components/system/HasPerm.tsx +14 -0
  57. package/src/framework/components/system/index.tsx +29 -0
  58. package/src/framework/fields/FieldBoolean/index.d.ts +9 -0
  59. package/src/framework/fields/FieldBoolean/index.jsx +73 -0
  60. package/src/framework/fields/FieldDate/index.d.ts +23 -0
  61. package/src/framework/fields/FieldDate/index.jsx +116 -0
  62. package/src/framework/fields/FieldDateRange/index.d.ts +22 -0
  63. package/src/framework/fields/FieldDateRange/index.jsx +103 -0
  64. package/src/framework/fields/FieldDictSelect/index.d.ts +12 -0
  65. package/src/framework/fields/FieldDictSelect/index.jsx +16 -0
  66. package/src/framework/fields/FieldEditor/index.d.ts +14 -0
  67. package/src/framework/fields/FieldEditor/index.jsx +59 -0
  68. package/src/framework/fields/FieldNumberRange/index.d.ts +10 -0
  69. package/src/framework/fields/FieldNumberRange/index.jsx +55 -0
  70. package/src/framework/fields/FieldPercent/index.d.ts +8 -0
  71. package/src/framework/fields/FieldPercent/index.jsx +30 -0
  72. package/src/framework/fields/FieldRemoteSelect/index.d.ts +44 -0
  73. package/src/framework/fields/FieldRemoteSelect/index.jsx +125 -0
  74. package/src/framework/fields/FieldRemoteSelectMultiple/index.d.ts +20 -0
  75. package/src/framework/fields/FieldRemoteSelectMultiple/index.jsx +85 -0
  76. package/src/framework/fields/FieldRemoteSelectMultipleInline/index.d.ts +21 -0
  77. package/src/framework/fields/FieldRemoteSelectMultipleInline/index.jsx +88 -0
  78. package/src/framework/fields/FieldRemoteTree/index.d.ts +20 -0
  79. package/src/framework/fields/FieldRemoteTree/index.jsx +50 -0
  80. package/src/framework/fields/FieldRemoteTreeCascader/index.d.ts +18 -0
  81. package/src/framework/fields/FieldRemoteTreeCascader/index.jsx +59 -0
  82. package/src/framework/fields/FieldRemoteTreeSelect/index.d.ts +19 -0
  83. package/src/framework/fields/FieldRemoteTreeSelect/index.jsx +57 -0
  84. package/src/framework/fields/FieldRemoteTreeSelectMultiple/index.d.ts +20 -0
  85. package/src/framework/fields/FieldRemoteTreeSelectMultiple/index.jsx +62 -0
  86. package/src/framework/fields/FieldSysOrgTree/index.d.ts +9 -0
  87. package/src/framework/fields/FieldSysOrgTree/index.jsx +20 -0
  88. package/src/framework/fields/FieldSysOrgTreeSelect/index.d.ts +9 -0
  89. package/src/framework/fields/FieldSysOrgTreeSelect/index.jsx +22 -0
  90. package/src/framework/fields/FieldTable/index.d.ts +14 -0
  91. package/src/framework/fields/FieldTable/index.jsx +108 -0
  92. package/src/framework/fields/FieldTable/styles.less +29 -0
  93. package/src/framework/fields/FieldTableSelect/index.d.ts +19 -0
  94. package/src/framework/fields/FieldTableSelect/index.jsx +60 -0
  95. package/src/framework/fields/FieldUploadFile/index.d.ts +31 -0
  96. package/src/framework/fields/FieldUploadFile/index.jsx +139 -0
  97. package/src/framework/fields/index.ts +22 -0
  98. package/src/framework/fields/types.ts +16 -0
  99. package/src/framework/index.ts +5 -0
  100. package/src/framework/pages/LoginPage.d.ts +16 -0
  101. package/src/framework/pages/LoginPage.jsx +135 -0
  102. package/src/framework/pages/LoginPage.less +53 -0
  103. package/src/framework/pages/LoginPageUtils.ts +36 -0
  104. package/src/framework/pages/index.ts +2 -0
  105. package/src/framework/utils/ArrUtils.ts +229 -0
  106. package/src/framework/utils/ColorsUtils.ts +378 -0
  107. package/src/framework/utils/DateUtils.ts +187 -0
  108. package/src/framework/utils/DeviceUtils.ts +46 -0
  109. package/src/framework/utils/DomUtils.ts +50 -0
  110. package/src/framework/utils/EventBusUtils.ts +144 -0
  111. package/src/framework/utils/Logger.ts +40 -0
  112. package/src/framework/utils/MessageUtils.tsx +170 -0
  113. package/src/framework/utils/ObjectUtils.ts +118 -0
  114. package/src/framework/utils/StorageUtils.ts +50 -0
  115. package/src/framework/utils/StringUtils.ts +436 -0
  116. package/src/framework/utils/TreeUtils.ts +251 -0
  117. package/src/framework/utils/UrlUtils.ts +152 -0
  118. package/src/framework/utils/UuidUtils.ts +88 -0
  119. package/src/framework/utils/ValidateUtils.ts +28 -0
  120. package/src/framework/utils/index.ts +15 -0
  121. package/src/framework/utils/system/DictUtils.ts +97 -0
  122. package/src/framework/utils/system/FormRegistryUtils.ts +77 -0
  123. package/src/framework/utils/system/HttpUtils.ts +247 -0
  124. package/src/framework/utils/system/PageUtils.ts +163 -0
  125. package/src/framework/utils/system/PermUtils.ts +79 -0
  126. package/src/framework/utils/system/SysUtils.ts +97 -0
  127. package/src/framework/utils/system/ThemeUtils.ts +27 -0
  128. package/src/framework/utils/system/index.ts +7 -0
  129. package/src/framework/views/ViewApproveStatus/index.d.ts +3 -0
  130. package/src/framework/views/ViewApproveStatus/index.jsx +21 -0
  131. package/src/framework/views/ViewBoolean/index.d.ts +3 -0
  132. package/src/framework/views/ViewBoolean/index.jsx +4 -0
  133. package/src/framework/views/ViewBooleanEnableDisable/index.d.ts +5 -0
  134. package/src/framework/views/ViewBooleanEnableDisable/index.jsx +15 -0
  135. package/src/framework/views/ViewFile/index.d.ts +10 -0
  136. package/src/framework/views/ViewFile/index.jsx +49 -0
  137. package/src/framework/views/ViewFileButton/index.d.ts +10 -0
  138. package/src/framework/views/ViewFileButton/index.jsx +22 -0
  139. package/src/framework/views/ViewImage/index.d.ts +6 -0
  140. package/src/framework/views/ViewImage/index.jsx +60 -0
  141. package/src/framework/views/ViewPassword/index.d.ts +5 -0
  142. package/src/framework/views/ViewPassword/index.jsx +24 -0
  143. package/src/framework/views/ViewProcessInstanceProgress/index.d.ts +12 -0
  144. package/src/framework/views/ViewProcessInstanceProgress/index.jsx +97 -0
  145. package/src/framework/views/ViewProcessInstanceProgressButton/index.d.ts +6 -0
  146. package/src/framework/views/ViewProcessInstanceProgressButton/index.jsx +24 -0
  147. package/src/framework/views/ViewText/index.d.ts +16 -0
  148. package/src/framework/views/ViewText/index.jsx +42 -0
  149. package/src/framework/views/index.ts +12 -0
  150. package/src/framework/views/types.ts +26 -0
  151. package/src/index.ts +2 -0
  152. package/src/layouts/PageRender.d.ts +22 -0
  153. package/src/layouts/PageRender.jsx +90 -0
  154. package/src/layouts/admin/HeaderRight.jsx +104 -0
  155. package/src/layouts/admin/TabPageRender.jsx +158 -0
  156. package/src/layouts/admin/index.jsx +159 -0
  157. package/src/layouts/admin/index.less +65 -0
  158. package/src/layouts/index.jsx +187 -0
  159. package/src/layouts/index.less +24 -0
  160. package/src/loading.jsx +18 -0
  161. package/src/pages/404.jsx +13 -0
  162. package/src/pages/about.jsx +12 -0
  163. package/src/pages/index.jsx +10 -0
  164. package/src/pages/login.jsx +16 -0
  165. package/src/pages/system/api/ApiDoc.jsx +148 -0
  166. package/src/pages/system/api/index.jsx +267 -0
  167. package/src/pages/system/api/perm.jsx +69 -0
  168. package/src/pages/system/dict/Dict.jsx +67 -0
  169. package/src/pages/system/dict/DictItem.jsx +175 -0
  170. package/src/pages/system/dict/index.jsx +25 -0
  171. package/src/pages/system/file/index.jsx +147 -0
  172. package/src/pages/system/job/index.jsx +324 -0
  173. package/src/pages/system/log/index.jsx +77 -0
  174. package/src/pages/system/org/index.jsx +260 -0
  175. package/src/pages/system/role/index.jsx +302 -0
  176. package/src/pages/system/role/perm.jsx +107 -0
  177. package/src/pages/system/sysManual/index.jsx +126 -0
  178. package/src/pages/system/user/UserPerm.jsx +94 -0
  179. package/src/pages/system/user/index.jsx +253 -0
  180. package/src/pages/test/views.jsx +95 -0
  181. package/src/pages/ureport/index.jsx +16 -0
  182. package/src/pages/userCenter/ChangePassword.jsx +64 -0
  183. package/src/pages/userCenter/index.jsx +90 -0
  184. package/src/pages/userCenter/manual.jsx +57 -0
  185. package/src/pages/userCenter/message.jsx +103 -0
  186. package/src/style/global.less +51 -0
@@ -0,0 +1,53 @@
1
+ .login-page {
2
+ background-size: cover;
3
+ background-image: url("/admin/public/login_bg.jpg");
4
+ background-repeat: no-repeat;
5
+ background-position: bottom;
6
+
7
+ width: 100vw;
8
+ height: 100vh;
9
+
10
+
11
+ .login-content {
12
+ padding: 40px;
13
+ background: #15184b44;
14
+ width: 350px;
15
+ position: fixed;
16
+ right: 15%;
17
+ top: 20%;
18
+
19
+ .ant-form-item-label > label{
20
+ color: white !important;
21
+ }
22
+ }
23
+ }
24
+
25
+ @media screen and (max-width: 768px) {
26
+ .login-page {
27
+ padding-top: 30%;
28
+
29
+ .login-content {
30
+ width: calc(100% - 100px);
31
+ padding: 24px;
32
+ position: static;
33
+ margin: 0 auto;
34
+ }
35
+ }
36
+ }
37
+
38
+
39
+ .login-form {
40
+ max-width: 300px;
41
+ margin: 0 auto;
42
+ }
43
+
44
+
45
+
46
+
47
+ .login-page h1 {
48
+ margin-bottom: 50px;
49
+ text-align: center;
50
+ color: white !important;
51
+ font-size: x-large;
52
+ }
53
+
@@ -0,0 +1,36 @@
1
+ import {history} from 'umi';
2
+ import {EventBusUtils, HttpUtils, PageUtils} from "../utils";
3
+ /**
4
+ * 将登录页面的公共方法提取出来,方便自定义登录页面时使用
5
+ */
6
+ export class LoginPageUtils {
7
+
8
+
9
+ static postLogin(values) {
10
+ return new Promise((resolve, reject) => {
11
+ console.log('开始登录')
12
+ HttpUtils.postForm('/admin/auth/login', values).then(rs => {
13
+ console.log('登录结果', rs)
14
+ EventBusUtils.emit('loginSuccess')
15
+ let redirect = LoginPageUtils.getRedirect();
16
+ history.push(redirect)
17
+ resolve(rs)
18
+ }).catch(e => {
19
+ console.log('登录错误', e)
20
+ reject(e)
21
+ })
22
+ })
23
+
24
+ }
25
+
26
+ static getRedirect() {
27
+ let redirect = PageUtils.currentParams()['redirect'];
28
+ if (redirect) {
29
+ console.log('重定向参数', redirect)
30
+ redirect = decodeURIComponent(redirect)
31
+ }else {
32
+ redirect = '/'
33
+ }
34
+ return redirect;
35
+ }
36
+ }
@@ -0,0 +1,2 @@
1
+ export * from './LoginPage'
2
+ export * from './LoginPageUtils'
@@ -0,0 +1,229 @@
1
+ /**
2
+ * 数组工具类
3
+ * 提供了一系列对数组进行操作的静态方法。
4
+ */
5
+ export class ArrUtils {
6
+
7
+ /**
8
+ * 检查数组是否包含某个元素。
9
+ * @param arr 目标数组。
10
+ * @param item 要检查的元素。
11
+ * @returns 如果包含则返回 true,否则返回 false。
12
+ */
13
+ static contains<T>(arr: T[], item: T): boolean {
14
+ return arr.indexOf(item) !== -1;
15
+ }
16
+
17
+ /**
18
+ * 检查数组是否包含至少一个指定的元素。
19
+ * 注意:原始实现中的 this.contains(item) 逻辑错误,已修正为检查 arr 是否包含 item。
20
+ *
21
+ * @param arr 目标数组。
22
+ * @param items 要检查的一个或多个元素。
23
+ * @returns 如果包含任意一个元素则返回 true,否则返回 false。
24
+ */
25
+ static containsAny<T>(arr: T[], ...items: T[]): boolean {
26
+ for (const item of items) {
27
+ // 这里调用自身的静态方法
28
+ if (ArrUtils.contains(arr, item)) {
29
+ return true;
30
+ }
31
+ }
32
+ return false;
33
+ }
34
+
35
+ /**
36
+ * 在数组末尾添加一个元素。
37
+ * @param arr 目标数组。
38
+ * @param item 要添加的元素。
39
+ */
40
+ static add<T>(arr: T[], item: T): void {
41
+ arr.push(item);
42
+ }
43
+
44
+ /**
45
+ * 在数组的指定索引处添加一个元素。
46
+ * @param arr 目标数组。
47
+ * @param index 插入位置的索引。
48
+ * @param item 要添加的元素。
49
+ */
50
+ static addAt<T>(arr: T[], index: number, item: T): void {
51
+ arr.splice(index, 0, item);
52
+ }
53
+
54
+ /**
55
+ * 将另一个数组的所有元素追加到目标数组的尾部。
56
+ * @param arr 目标数组。
57
+ * @param items 要追加的元素数组。
58
+ */
59
+ static addAll<T>(arr: T[], items: T[]): void {
60
+ // 使用 ES6 的 spread 运算符或 push.apply 效率更高
61
+ arr.push(...items);
62
+ }
63
+
64
+ /**
65
+ * 移除数组指定索引处的元素。
66
+ * @param arr 目标数组。
67
+ * @param index 要移除元素的索引。
68
+ */
69
+ static removeAt<T>(arr: T[], index: number): void {
70
+ if (index >= 0 && index < arr.length) {
71
+ arr.splice(index, 1);
72
+ }
73
+ }
74
+
75
+ /**
76
+ * 移除数组中第一个匹配的元素。
77
+ * @param arr 目标数组。
78
+ * @param item 要移除的元素。
79
+ */
80
+ static remove<T>(arr: T[], item: T): void {
81
+ const index = arr.indexOf(item);
82
+ if (index !== -1) {
83
+ // 这里调用自身的静态方法
84
+ ArrUtils.removeAt(arr, index);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * 清空数组。
90
+ * @param arr 目标数组。
91
+ */
92
+ static clear<T>(arr: T[]): void {
93
+ arr.length = 0;
94
+ }
95
+
96
+ /**
97
+ * 截取数组的一个子集。
98
+ * @param arr 目标数组。
99
+ * @param fromIndex 开始索引(包含)。
100
+ * @param toIndex 结束索引(不包含)。
101
+ * @returns 截取后的新数组。
102
+ */
103
+ static sub<T>(arr: T[], fromIndex?: number, toIndex?: number): T[] {
104
+ return arr.slice(fromIndex, toIndex);
105
+ }
106
+
107
+ /**
108
+ * 交换数组中两个元素的位置。
109
+ * @param arr 目标数组。
110
+ * @param item1 元素1。
111
+ * @param item2 元素2。
112
+ */
113
+ static swap<T>(arr: T[], item1: T, item2: T): void {
114
+ const index1 = arr.indexOf(item1);
115
+ const index2 = arr.indexOf(item2);
116
+
117
+ if (index1 !== -1 && index2 !== -1) {
118
+ // 使用解构赋值进行交换,更简洁
119
+ [arr[index1], arr[index2]] = [arr[index2], arr[index1]];
120
+ }
121
+ }
122
+
123
+ /**
124
+ * 在数组的指定索引处插入一个元素(与 addAt 相同,保留以兼容原 API)。
125
+ * @param arr 目标数组。
126
+ * @param index 插入位置的索引。
127
+ * @param item 要插入的元素。
128
+ */
129
+ static insert<T>(arr: T[], index: number, item: T): void {
130
+ ArrUtils.addAt(arr, index, item);
131
+ }
132
+
133
+ /**
134
+ * 如果元素不存在于数组中,则将其添加到数组末尾。
135
+ * @param arr 目标数组。
136
+ * @param item 要添加的元素。
137
+ */
138
+ static pushIfNotExist<T>(arr: T[], item: T): void {
139
+ if (!ArrUtils.contains(arr, item)) {
140
+ arr.push(item);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * 将新数组中的所有元素添加到目标数组的末尾。
146
+ * @param arr 目标数组。
147
+ * @param newArr 要添加的元素数组。
148
+ */
149
+ static pushAll<T>(arr: T[], newArr: T[]): void {
150
+ ArrUtils.addAll(arr, newArr);
151
+ }
152
+
153
+ /**
154
+ * 获取对象数组中某一属性值最大的对象。
155
+ * 使用泛型 K 约束 key 必须是 T 的属性,并确保属性值可以进行数值比较。
156
+ *
157
+ * @param arr 对象数组。
158
+ * @param key 用于比较的属性名。
159
+ * @returns 属性值最大的对象,如果数组为空则返回 undefined。
160
+ */
161
+ static maxBy<T extends Record<K, any>, K extends keyof T>(
162
+ arr: T[],
163
+ key: K
164
+ ): T | undefined {
165
+ if (arr == null || arr.length === 0) {
166
+ return undefined;
167
+ }
168
+
169
+ let maxElement: T | undefined = undefined;
170
+ let maxValue: number = -Infinity;
171
+
172
+ for (const element of arr) {
173
+ // 运行时类型检查(虽然 TS 层面已约束 K,但值可能不是数字)
174
+ const value = element[key];
175
+
176
+ // 确保比较的是数字类型
177
+ if (typeof value === 'number' && value > maxValue) {
178
+ maxValue = value;
179
+ maxElement = element;
180
+ } else if (typeof value !== 'number') {
181
+ // 可选:如果遇到非数字,可以跳过或根据需求抛出错误/记录警告
182
+ // console.warn(`Element with key ${String(key)} has non-numeric value: ${value}`);
183
+ }
184
+ }
185
+
186
+ return maxElement;
187
+ }
188
+
189
+ /**
190
+ * 对数组进行去重。
191
+ * @param arr 目标数组。
192
+ * @returns 去重后的新数组。
193
+ */
194
+ static unique<T>(arr: T[]): T[] {
195
+ return [...new Set(arr)];
196
+ }
197
+ }
198
+
199
+ // 示例用法(可选)
200
+ /*
201
+ interface User {
202
+ id: number;
203
+ score: number;
204
+ name: string;
205
+ }
206
+
207
+ const users: User[] = [
208
+ { id: 1, score: 90, name: 'Alice' },
209
+ { id: 2, score: 95, name: 'Bob' },
210
+ { id: 3, score: 88, name: 'Charlie' },
211
+ ];
212
+
213
+ const maxUser = ArrUtil.maxBy(users, 'score');
214
+ console.log(maxUser); // { id: 2, score: 95, name: 'Bob' }
215
+
216
+ const numbers = [1, 2, 2, 3, 4, 1];
217
+ const uniqueNumbers = ArrUtil.unique(numbers);
218
+ console.log(uniqueNumbers); // [1, 2, 3, 4]
219
+
220
+ let list = [10, 20];
221
+ ArrUtil.register(list, 30);
222
+ console.log(list); // [10, 20, 30]
223
+
224
+ ArrUtil.remove(list, 20);
225
+ console.log(list); // [10, 30]
226
+
227
+ ArrUtil.addAll(list, [40, 50]);
228
+ console.log(list); // [10, 30, 40, 50]
229
+ */
@@ -0,0 +1,378 @@
1
+ /**
2
+ * forked form https://github.com/quasarframework/quasar
3
+ */
4
+
5
+ // 正则表达式用于匹配 rgba 格式的字符串
6
+ const reRGBA = /^rgb(a)?\((\d{1,3}),(\d{1,3}),(\d{1,3}),?([01]?\.?\d*?)?\)$/
7
+
8
+ /**
9
+ * RGB 颜色接口
10
+ * r, g, b 的范围是 [0, 255]
11
+ * a 的范围是 [0, 100] (百分比)
12
+ */
13
+ export interface RGB {
14
+ r: number
15
+ g: number
16
+ b: number
17
+ a?: number // 可选的 alpha (百分比)
18
+ }
19
+
20
+ /**
21
+ * HSV 颜色接口
22
+ * h 的范围是 [0, 360]
23
+ * s, v 的范围是 [0, 100] (百分比)
24
+ * a 的范围是 [0, 100] (百分比)
25
+ */
26
+ export interface HSV {
27
+ h: number
28
+ s: number
29
+ v: number
30
+ a?: number // 可选的 alpha (百分比)
31
+ }
32
+
33
+ /**
34
+ * 颜色工具类,提供颜色相关的转换和操作方法。
35
+ */
36
+ export class ColorsUtils {
37
+ /**
38
+ * 将 RGB 颜色对象转换为十六进制字符串 (包含可选的 alpha 值).
39
+ * @param color RGB 颜色对象
40
+ * @returns 十六进制颜色字符串, 如 '#RRGGBB' 或 '#RRGGBBAA'
41
+ */
42
+ static rgbToHex (color: RGB): string {
43
+ let { r, g, b, a } = color
44
+ const alpha = a !== void 0
45
+
46
+ r = Math.round(r)
47
+ g = Math.round(g)
48
+ b = Math.round(b)
49
+
50
+ if (
51
+ r > 255
52
+ || g > 255
53
+ || b > 255
54
+ || (alpha && a! > 100)
55
+ ) {
56
+ throw new TypeError('Expected 3 numbers below 256 (and optionally one below 100)')
57
+ }
58
+
59
+ const alphaHex = alpha
60
+ ? (Math.round(255 * a! / 100) | 1 << 8).toString(16).slice(1)
61
+ : ''
62
+
63
+ return '#' + ((b | g << 8 | r << 16) | 1 << 24).toString(16).slice(1) + alphaHex
64
+ }
65
+
66
+ /**
67
+ * 将 RGB 颜色对象转换为 rgb() 或 rgba() 字符串.
68
+ * @param color RGB 颜色对象
69
+ * @returns rgb() 或 rgba() 颜色字符串
70
+ */
71
+ static rgbToString (color: RGB): string {
72
+ const { r, g, b, a } = color
73
+ return `rgb${a !== void 0 ? 'a' : ''}(${r},${g},${b}${a !== void 0 ? ',' + (a / 100) : ''})`
74
+ }
75
+
76
+ /**
77
+ * 将十六进制颜色字符串转换为 RGB 颜色对象.
78
+ * 支持 '#RGB', '#RGBA', '#RRGGBB', '#RRGGBBAA' 格式.
79
+ * @param hex 十六进制颜色字符串
80
+ * @returns RGB 颜色对象
81
+ */
82
+ static hexToRgb (hex: string): RGB {
83
+ if (typeof hex !== 'string') {
84
+ throw new TypeError('Expected a string')
85
+ }
86
+
87
+ hex = hex.replace(/^#/, '')
88
+
89
+ if (hex.length === 3) {
90
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]
91
+ } else if (hex.length === 4) {
92
+ // 确保 alpha 也被扩展
93
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]
94
+ }
95
+
96
+ const num = parseInt(hex, 16)
97
+
98
+ return hex.length > 6
99
+ ? { r: num >> 24 & 255, g: num >> 16 & 255, b: num >> 8 & 255, a: Math.round((num & 255) / 2.55) }
100
+ : { r: num >> 16, g: num >> 8 & 255, b: num & 255 }
101
+ }
102
+
103
+ /**
104
+ * 将 HSV 颜色对象转换为 RGB 颜色对象.
105
+ * @param color HSV 颜色对象
106
+ * @returns RGB 颜色对象
107
+ */
108
+ static hsvToRgb (color: HSV): RGB {
109
+ let r: number = 0, g: number = 0, b: number = 0
110
+ let { h, s, v, a } = color
111
+ s = s / 100
112
+ v = v / 100
113
+
114
+ h = h / 360
115
+ const
116
+ i = Math.floor(h * 6),
117
+ f = h * 6 - i,
118
+ p = v * (1 - s),
119
+ q = v * (1 - f * s),
120
+ t = v * (1 - (1 - f) * s)
121
+
122
+ switch (i % 6) {
123
+ case 0:
124
+ r = v
125
+ g = t
126
+ b = p
127
+ break
128
+ case 1:
129
+ r = q
130
+ g = v
131
+ b = p
132
+ break
133
+ case 2:
134
+ r = p
135
+ g = v
136
+ b = t
137
+ break
138
+ case 3:
139
+ r = p
140
+ g = q
141
+ b = v
142
+ break
143
+ case 4:
144
+ r = t
145
+ g = p
146
+ b = v
147
+ break
148
+ case 5:
149
+ r = v
150
+ g = p
151
+ b = q
152
+ break
153
+ }
154
+
155
+ return {
156
+ r: Math.round(r * 255),
157
+ g: Math.round(g * 255),
158
+ b: Math.round(b * 255),
159
+ a
160
+ }
161
+ }
162
+
163
+ /**
164
+ * 将 RGB 颜色对象转换为 HSV 颜色对象.
165
+ * @param color RGB 颜色对象
166
+ * @returns HSV 颜色对象
167
+ */
168
+ static rgbToHsv (color: RGB): HSV {
169
+ const { r, g, b, a } = color
170
+ const
171
+ max = Math.max(r, g, b),
172
+ min = Math.min(r, g, b),
173
+ d = max - min,
174
+ s = (max === 0 ? 0 : d / max),
175
+ v = max / 255
176
+ let h: number
177
+
178
+ switch (max) {
179
+ case min:
180
+ h = 0
181
+ break
182
+ case r:
183
+ h = (g - b) + d * (g < b ? 6 : 0)
184
+ h /= 6 * d
185
+ break
186
+ case g:
187
+ h = (b - r) + d * 2
188
+ h /= 6 * d
189
+ break
190
+ case b:
191
+ h = (r - g) + d * 4
192
+ h /= 6 * d
193
+ break
194
+ default:
195
+ h = 0 // 理论上不会走到这里
196
+ }
197
+
198
+ return {
199
+ h: Math.round(h * 360),
200
+ s: Math.round(s * 100),
201
+ v: Math.round(v * 100),
202
+ a
203
+ }
204
+ }
205
+
206
+ /**
207
+ * 将颜色文本 (hex 或 rgb/rgba) 转换为 RGB 颜色对象.
208
+ * @param str 颜色字符串
209
+ * @returns RGB 颜色对象
210
+ */
211
+ static textToRgb (str: string): RGB {
212
+ if (typeof str !== 'string') {
213
+ throw new TypeError('Expected a string')
214
+ }
215
+
216
+ const color = str.replace(/ /g, '')
217
+
218
+ const m = reRGBA.exec(color)
219
+
220
+ if (m === null) {
221
+ return this.hexToRgb(color)
222
+ }
223
+
224
+ const rgb: RGB = {
225
+ r: Math.min(255, parseInt(m[2], 10)),
226
+ g: Math.min(255, parseInt(m[3], 10)),
227
+ b: Math.min(255, parseInt(m[4], 10))
228
+ }
229
+
230
+ if (m[1]) {
231
+ const alpha = parseFloat(m[5])
232
+ // alpha 值在 [0, 1],需要转换为 [0, 100]
233
+ rgb.a = Math.min(1, isNaN(alpha) === true ? 1 : alpha) * 100
234
+ }
235
+
236
+ return rgb
237
+ }
238
+
239
+ /**
240
+ * 调亮或调暗颜色.
241
+ * percent > 0 为调亮 (lighten), percent < 0 为调暗 (darken).
242
+ * @param color 颜色字符串
243
+ * @param percent 百分比偏移量, 范围通常为 [-100, 100]
244
+ * @returns 新的十六进制颜色字符串
245
+ */
246
+ static lighten (color: string, percent: number): string {
247
+ if (typeof color !== 'string') {
248
+ throw new TypeError('Expected a string as color')
249
+ }
250
+ if (typeof percent !== 'number') {
251
+ throw new TypeError('Expected a numeric percent')
252
+ }
253
+
254
+ const rgb = this.textToRgb(color)
255
+ const
256
+ t = percent < 0 ? 0 : 255,
257
+ p = Math.abs(percent) / 100,
258
+ R = rgb.r,
259
+ G = rgb.g,
260
+ B = rgb.b
261
+
262
+ return '#' + (
263
+ 0x1000000 + (Math.round((t - R) * p) + R) * 0x10000
264
+ + (Math.round((t - G) * p) + G) * 0x100
265
+ + (Math.round((t - B) * p) + B)
266
+ ).toString(16).slice(1)
267
+ }
268
+
269
+ /**
270
+ * 计算颜色的亮度 (Luminosity).
271
+ * @param color 颜色字符串或 RGB 对象
272
+ * @returns 颜色的亮度值 (0 到 1)
273
+ */
274
+ static luminosity (color: string | RGB): number {
275
+ if (typeof color !== 'string' && (!color || color.r === void 0)) {
276
+ throw new TypeError('Expected a string or a {r, g, b} object as color')
277
+ }
278
+
279
+ const
280
+ rgb = typeof color === 'string' ? this.textToRgb(color) : color,
281
+ r = rgb.r / 255,
282
+ g = rgb.g / 255,
283
+ b = rgb.b / 255,
284
+ // 标准化 R, G, B 值
285
+ R = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4),
286
+ G = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4),
287
+ B = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4)
288
+
289
+ // W3C WCAG 2.0 亮度公式
290
+ return 0.2126 * R + 0.7152 * G + 0.0722 * B
291
+ }
292
+
293
+ /**
294
+ * 计算颜色的感知亮度 (Brightness).
295
+ * @param color 颜色字符串或 RGB 对象
296
+ * @returns 颜色的感知亮度值 (0 到 255)
297
+ */
298
+ static brightness (color: string | RGB): number {
299
+ if (typeof color !== 'string' && (!color || color.r === void 0)) {
300
+ throw new TypeError('Expected a string or a {r, g, b} object as color')
301
+ }
302
+
303
+ const rgb = typeof color === 'string'
304
+ ? this.textToRgb(color)
305
+ : color
306
+
307
+ // 根据人眼感知度的加权平均 (常用公式)
308
+ return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000
309
+ }
310
+
311
+ /**
312
+ * 将前景颜色 (fgColor) 混合到背景颜色 (bgColor) 上。
313
+ * @param fgColor 前景颜色字符串或 RGB 对象
314
+ * @param bgColor 背景颜色字符串或 RGB 对象
315
+ * @returns 如果 fgColor 是字符串则返回十六进制字符串,否则返回 RGB 对象
316
+ */
317
+ static blend (fgColor: string | RGB, bgColor: string | RGB): string | RGB {
318
+ if (typeof fgColor !== 'string' && (!fgColor || fgColor.r === void 0)) {
319
+ throw new TypeError('Expected a string or a {r, g, b[, a]} object as fgColor')
320
+ }
321
+
322
+ if (typeof bgColor !== 'string' && (!bgColor || bgColor.r === void 0)) {
323
+ throw new TypeError('Expected a string or a {r, g, b[, a]} object as bgColor')
324
+ }
325
+
326
+ const
327
+ rgb1 = typeof fgColor === 'string' ? this.textToRgb(fgColor) : fgColor,
328
+ r1 = rgb1.r / 255,
329
+ g1 = rgb1.g / 255,
330
+ b1 = rgb1.b / 255,
331
+ a1 = rgb1.a !== void 0 ? rgb1.a / 100 : 1, // 前景 alpha (0-1)
332
+ rgb2 = typeof bgColor === 'string' ? this.textToRgb(bgColor) : bgColor,
333
+ r2 = rgb2.r / 255,
334
+ g2 = rgb2.g / 255,
335
+ b2 = rgb2.b / 255,
336
+ a2 = rgb2.a !== void 0 ? rgb2.a / 100 : 1, // 背景 alpha (0-1)
337
+
338
+ // Porter-Duff Over 运算符
339
+ a = a1 + a2 * (1 - a1), // 混合后的总 alpha
340
+ r = Math.round(((r1 * a1 + r2 * a2 * (1 - a1)) / a) * 255),
341
+ g = Math.round(((g1 * a1 + g2 * a2 * (1 - a1)) / a) * 255),
342
+ b = Math.round(((b1 * a1 + b2 * a2 * (1 - a1)) / a) * 255)
343
+
344
+ const ret: RGB = { r, g, b, a: Math.round(a * 100) } // a 转换回 (0-100)
345
+
346
+ // 如果前景颜色输入是字符串,则返回十六进制字符串,否则返回 RGB 对象
347
+ return typeof fgColor === 'string'
348
+ ? this.rgbToHex(ret)
349
+ : ret
350
+ }
351
+
352
+ /**
353
+ * 改变颜色字符串的 alpha 透明度。
354
+ * @param color 颜色字符串 (如 '#RRGGBB' 或 'rgb(r,g,b)')
355
+ * @param offset alpha 的偏移量, 范围 [-1, 1]。正值增加透明度,负值减少透明度。
356
+ * @returns 带有新 alpha 值的十六进制颜色字符串 (#RRGGBBAA)
357
+ */
358
+ static changeAlpha (color: string, offset: number): string {
359
+ if (typeof color !== 'string') {
360
+ throw new TypeError('Expected a string as color')
361
+ }
362
+
363
+ if (offset === void 0 || offset < -1 || offset > 1) {
364
+ throw new TypeError('Expected offset to be between -1 and 1')
365
+ }
366
+
367
+ const { r, g, b, a } = this.textToRgb(color)
368
+ // 当前 alpha (0-1)
369
+ const alpha = a !== void 0 ? a / 100 : 1 // 如果没有 alpha,默认为 1 (100%)
370
+
371
+ // 新的 alpha (0-100)
372
+ const newAlpha = Math.round(Math.min(1, Math.max(0, alpha + offset)) * 100)
373
+
374
+ return this.rgbToHex({
375
+ r, g, b, a: newAlpha
376
+ })
377
+ }
378
+ }