@jiangood/open-admin 2.5.2 → 2.5.6

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 (108) hide show
  1. package/package.json +3 -2
  2. package/src/.umi-production/appData.json +78 -77
  3. package/src/.umi-production/core/route.tsx +6 -6
  4. package/src/framework/GlobalData.d.ts +8 -0
  5. package/src/framework/GlobalData.js +33 -0
  6. package/src/framework/{components/system → biz}/HasPerm.tsx +1 -1
  7. package/src/framework/{components/OrgTree/index.tsx → biz/OrgTree.tsx} +12 -2
  8. package/src/framework/{components/system/ButtonList.tsx → biz/PermActions.tsx} +6 -5
  9. package/src/framework/{components/RoleTree/index.tsx → biz/RoleTree.tsx} +13 -3
  10. package/src/framework/{components/system → biz}/index.tsx +4 -2
  11. package/src/framework/components/ContextMenu/index.tsx +1 -1
  12. package/src/framework/components/DownloadModal/index.tsx +332 -0
  13. package/src/framework/components/ErrorBoundary/index.tsx +18 -0
  14. package/src/framework/components/NamedIcon/{index.jsx → index.tsx} +8 -3
  15. package/src/framework/components/Page/index.tsx +2 -2
  16. package/src/framework/components/ProTable/index.tsx +3 -10
  17. package/src/framework/components/index.ts +2 -6
  18. package/src/framework/fields/BaseRemoteSelect/index.tsx +2 -3
  19. package/src/framework/fields/FieldEditor/index.tsx +35 -44
  20. package/src/framework/fields/FieldNumberRange/index.tsx +2 -2
  21. package/src/framework/fields/FieldRemoteTree/index.tsx +1 -1
  22. package/src/framework/fields/FieldTable/index.tsx +20 -22
  23. package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
  24. package/src/framework/fields/index.ts +1 -3
  25. package/src/framework/index.ts +2 -0
  26. package/src/framework/utils/ArrayUtils.d.ts +6 -0
  27. package/src/framework/utils/ArrayUtils.js +30 -0
  28. package/src/framework/utils/DateUtils.d.ts +20 -0
  29. package/src/framework/utils/DateUtils.js +81 -0
  30. package/src/framework/utils/DeviceUtils.d.ts +3 -0
  31. package/src/framework/utils/DeviceUtils.js +10 -0
  32. package/src/framework/utils/EventBus.d.ts +6 -0
  33. package/src/framework/utils/EventBus.js +51 -0
  34. package/src/framework/utils/MessageUtils.d.ts +10 -0
  35. package/src/framework/utils/MessageUtils.js +43 -0
  36. package/src/framework/utils/ObjectUtils.d.ts +14 -0
  37. package/src/framework/utils/ObjectUtils.js +12 -0
  38. package/src/framework/utils/StorageUtils.d.ts +4 -0
  39. package/src/framework/utils/{StorageUtils.ts → StorageUtils.js} +7 -11
  40. package/src/framework/utils/StringUtils.d.ts +17 -0
  41. package/src/framework/utils/StringUtils.js +107 -0
  42. package/src/framework/utils/TreeUtils.d.ts +13 -0
  43. package/src/framework/utils/TreeUtils.js +63 -0
  44. package/src/framework/utils/UrlUtils.d.ts +6 -0
  45. package/src/framework/utils/UrlUtils.js +71 -0
  46. package/src/framework/utils/index.ts +1 -5
  47. package/src/framework/utils/system/DictUtils.d.ts +20 -0
  48. package/src/framework/utils/system/DictUtils.js +31 -0
  49. package/src/framework/utils/system/FormRegistry.d.ts +8 -0
  50. package/src/framework/utils/system/FormRegistry.js +31 -0
  51. package/src/framework/utils/system/HttpUtils.d.ts +14 -0
  52. package/src/framework/utils/system/HttpUtils.js +87 -0
  53. package/src/framework/utils/system/PageUtils.d.ts +14 -0
  54. package/src/framework/utils/system/PageUtils.js +59 -0
  55. package/src/framework/utils/system/PermUtils.d.ts +5 -0
  56. package/src/framework/utils/system/PermUtils.js +27 -0
  57. package/src/framework/utils/system/ThemeUtils.d.ts +6 -0
  58. package/src/framework/utils/system/{ThemeUtils.ts → ThemeUtils.js} +19 -19
  59. package/src/framework/utils/system/index.ts +1 -2
  60. package/src/framework/views/ViewFile/index.tsx +1 -2
  61. package/src/framework/views/ViewImage/index.tsx +3 -3
  62. package/src/framework/views/{ViewBooleanEnableDisable → ViewSwitch}/index.tsx +1 -1
  63. package/src/framework/views/index.ts +3 -4
  64. package/src/layouts/admin/HeaderRight.jsx +32 -44
  65. package/src/layouts/admin/index.jsx +5 -29
  66. package/src/layouts/index.jsx +26 -17
  67. package/src/pages/about.jsx +24 -4
  68. package/src/pages/{userCenter/ChangePassword.jsx → account/changePassword.jsx} +1 -2
  69. package/src/pages/{userCenter → account}/index.jsx +2 -6
  70. package/src/pages/forceUpdatePwd.jsx +89 -0
  71. package/src/pages/login.jsx +5 -5
  72. package/src/pages/system/dict/index.jsx +6 -6
  73. package/src/pages/system/file/index.jsx +4 -4
  74. package/src/pages/system/job/index.jsx +7 -3
  75. package/src/pages/system/org/index.jsx +2 -2
  76. package/src/pages/system/role/index.jsx +4 -4
  77. package/src/pages/system/role/{perm.jsx → rolePerm.jsx} +2 -2
  78. package/src/pages/system/user/index.jsx +4 -4
  79. package/src/framework/components/DownloadFileButton/index.tsx +0 -33
  80. package/src/framework/components/NamedIcon/index.d.ts +0 -7
  81. package/src/framework/components/ProTable/utils/index.ts +0 -62
  82. package/src/framework/fields/FieldRemoteSelectMultipleInline/index.tsx +0 -33
  83. package/src/framework/fields/FieldRemoteTreeSelectMultiple/index.tsx +0 -43
  84. package/src/framework/utils/ArrayUtils.ts +0 -186
  85. package/src/framework/utils/ColorsUtils.ts +0 -378
  86. package/src/framework/utils/DateUtils.ts +0 -187
  87. package/src/framework/utils/DeviceUtils.ts +0 -46
  88. package/src/framework/utils/DomUtils.ts +0 -50
  89. package/src/framework/utils/EventBusUtils.ts +0 -143
  90. package/src/framework/utils/Logger.ts +0 -40
  91. package/src/framework/utils/MessageUtils.tsx +0 -90
  92. package/src/framework/utils/ObjectUtils.ts +0 -118
  93. package/src/framework/utils/StringUtils.ts +0 -436
  94. package/src/framework/utils/TreeUtils.ts +0 -251
  95. package/src/framework/utils/UrlUtils.ts +0 -152
  96. package/src/framework/utils/UuidUtils.ts +0 -88
  97. package/src/framework/utils/ValidateUtils.ts +0 -28
  98. package/src/framework/utils/system/DictUtils.ts +0 -96
  99. package/src/framework/utils/system/FormRegistryUtils.ts +0 -76
  100. package/src/framework/utils/system/HttpUtils.ts +0 -251
  101. package/src/framework/utils/system/PageUtils.ts +0 -163
  102. package/src/framework/utils/system/PermUtils.ts +0 -79
  103. package/src/framework/utils/system/SysUtils.ts +0 -105
  104. package/src/framework/views/ViewProcessInstanceProgress/index.tsx +0 -98
  105. package/src/framework/views/ViewProcessInstanceProgressButton/index.tsx +0 -23
  106. package/src/pages/userCenter/message.jsx +0 -105
  107. /package/src/framework/{components → views}/ViewRange/index.tsx +0 -0
  108. /package/src/pages/system/user/{UserPerm.jsx → userPerm.jsx} +0 -0
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import {Editor as TinyMceEditor} from '@tinymce/tinymce-react';
3
- import {SysUtils} from '../../utils';
3
+ import {UrlUtils} from '../../utils';
4
4
 
5
5
  /**
6
6
  * 富文本编辑器
@@ -9,52 +9,43 @@ import {SysUtils} from '../../utils';
9
9
  */
10
10
  export class FieldEditor extends React.Component {
11
11
  render() {
12
- const uploadUrl = SysUtils.contextPath('/admin/sysFile/upload');
13
- const jsUrl = SysUtils.contextPath('/admin/tinymce/tinymce.min.js');
12
+ const uploadUrl = UrlUtils.contextPath('/admin/sysFile/upload');
13
+ const jsUrl = UrlUtils.contextPath('/admin/tinymce/tinymce.min.js');
14
14
  const {value, onChange, height} = this.props;
15
15
 
16
16
  return (
17
- <>
18
- <TinyMceEditor
19
- initialValue={value}
20
- tinymceScriptSrc={jsUrl}
21
- init={{
22
- min_height: 300,
23
- language: 'zh_CN',
24
- height: height,
25
-
26
- // 上传图片
27
- images_upload_url: uploadUrl,
28
- promotion: false, // 不显示升级按钮(右上角)
29
- cache_suffix: '?v=v7.7',
30
-
31
- plugins: [
32
- 'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
33
- 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
34
- 'insertdatetime', 'media', 'table', 'code', 'help', 'wordcount',
35
- 'emoticons'
36
- ],
37
- image_description: false,
38
-
39
- // 设置图片上传对话框默认选中上传Tab
40
- setup: function (editor) {
41
- editor.on('OpenWindow', function(e) {
42
- const dialog = e.dialog;
43
- // 包含 dimensions 属性应该就是 上传图片的对话框
44
- if (dialog && dialog.getData().dimensions) {
45
- dialog.showTab("upload");
46
- }
47
- });
48
- },
49
-
50
- }}
51
- onChange={e => {
52
- if (onChange) {
53
- onChange(e.target.getContent());
54
- }
55
- }}
56
- />
57
- </>
17
+ <TinyMceEditor
18
+ initialValue={value}
19
+ tinymceScriptSrc={jsUrl}
20
+ init={{
21
+ min_height: 300,
22
+ language: 'zh_CN',
23
+ height: height,
24
+ images_upload_url: uploadUrl,
25
+ promotion: false,
26
+ cache_suffix: '?v=v7.7',
27
+ plugins: [
28
+ 'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
29
+ 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
30
+ 'insertdatetime', 'media', 'table', 'code', 'help', 'wordcount',
31
+ 'emoticons'
32
+ ],
33
+ image_description: false,
34
+ setup: function (editor) {
35
+ editor.on('OpenWindow', function(e) {
36
+ const dialog = e.dialog;
37
+ if (dialog && dialog.getData().dimensions) {
38
+ dialog.showTab("upload");
39
+ }
40
+ });
41
+ },
42
+ }}
43
+ onChange={e => {
44
+ if (onChange) {
45
+ onChange(e.target.getContent());
46
+ }
47
+ }}
48
+ />
58
49
  );
59
50
  }
60
51
  }
@@ -34,9 +34,9 @@ export class FieldNumberRange extends React.Component {
34
34
  }
35
35
 
36
36
  componentDidMount() {
37
- const {value, defaultValue} = this.props;
37
+ const {value, defaultValue, onChange} = this.props;
38
38
  if (value == null && defaultValue) {
39
- this.props.onChange && this.props.onChange(defaultValue);
39
+ onChange && onChange(defaultValue);
40
40
  }
41
41
  }
42
42
 
@@ -20,7 +20,7 @@ export class FieldRemoteTree extends BaseRemoteSelect {
20
20
  <Tree
21
21
  multiple
22
22
  checkable
23
- onCheck={(e) => this.props.onChange && this.props.onChange(e.checked)}
23
+ onCheck={(checkedInfo) => this.props.onChange && this.props.onChange(checkedInfo.checked)}
24
24
  checkedKeys={this.props.value}
25
25
  treeData={this.state.data}
26
26
  defaultExpandAll
@@ -2,7 +2,6 @@ import {Button, Input, Table} from 'antd'
2
2
  import React from 'react'
3
3
  import {DeleteOutlined, PlusOutlined} from "@ant-design/icons";
4
4
  import './styles.less'
5
- import {ArrayUtils} from "../../utils";
6
5
 
7
6
  /**
8
7
  * 可编辑表格
@@ -15,27 +14,27 @@ export class FieldTable extends React.Component {
15
14
  super(props);
16
15
 
17
16
  this.columns = this.props.columns.map(col => {
18
- if (col.render == null) {
19
- col.render = (v, record, index) => {
20
- return <Input value={v} onChange={(e) => this.onCellChange(index, col.dataIndex, e)}/>;
17
+ const newCol = { ...col };
18
+ const origRender = newCol.render;
19
+
20
+ if (origRender) {
21
+ newCol.render = (v, record, index) => {
22
+ const cmp = origRender(v, record, index);
23
+ return React.createElement(cmp.type, {
24
+ ...cmp.props,
25
+ value: v,
26
+ onChange: (e) => {
27
+ this.onCellChange(index, newCol.dataIndex, e);
28
+ }
29
+ });
21
30
  };
22
31
  } else {
23
- if (!col._oldRender) {
24
- col._oldRender = col.render;
25
- col.render = (v, record, index) => {
26
- const cmp = col._oldRender(v, record, index);
27
- return React.createElement(cmp.type,
28
- {
29
- ...cmp.props,
30
- value: v,
31
- onChange: (e) => {
32
- this.onCellChange(index, col.dataIndex, e);
33
- }
34
- });
35
- };
36
- }
32
+ newCol.render = (v, record, index) => {
33
+ return <Input value={v} onChange={(e) => this.onCellChange(index, newCol.dataIndex, e)}/>;
34
+ };
37
35
  }
38
- return col;
36
+
37
+ return newCol;
39
38
  });
40
39
 
41
40
  this.columns.push({
@@ -61,7 +60,7 @@ export class FieldTable extends React.Component {
61
60
  const row = dataSource[index];
62
61
 
63
62
  let v = e;
64
- if (e != null && e.hasOwnProperty('target')) {
63
+ if (e != null && typeof e === 'object' && 'target' in e) {
65
64
  v = e.target.value;
66
65
  }
67
66
 
@@ -79,8 +78,7 @@ export class FieldTable extends React.Component {
79
78
 
80
79
  remove = (record) => {
81
80
  const {dataSource} = this.state;
82
- ArrayUtils.remove(dataSource, record);
83
- this.setState({dataSource: [...dataSource]}, this.notifyParent);
81
+ this.setState({dataSource: dataSource.filter(item => item !== record)}, this.notifyParent);
84
82
  };
85
83
 
86
84
  notifyParent = () => {
@@ -3,7 +3,7 @@ import ImgCrop from "antd-img-crop";
3
3
  import {Modal, Upload, message} from "antd";
4
4
  import { UploadOutlined } from "@ant-design/icons";
5
5
  import {ViewFile} from "../../views";
6
- import {ObjectUtils, SysUtils} from "../../utils";
6
+ import {ObjectUtils, UrlUtils} from "../../utils";
7
7
 
8
8
  /**
9
9
  * 上传图片前裁切, 单张图片
@@ -33,7 +33,7 @@ export class FieldUploadFile extends React.Component {
33
33
  if (value && value.length > 0) {
34
34
  const arr = value.split(",");
35
35
  for (const id of arr) {
36
- const url = SysUtils.contextPath('/admin/sysFile/preview/' + id);
36
+ const url = UrlUtils.contextPath('/admin/sysFile/preview/' + id);
37
37
  const file = {id, url, uid: id, name: id, status: 'done', fileName: id};
38
38
  list.push(file);
39
39
  }
@@ -113,7 +113,7 @@ export class FieldUploadFile extends React.Component {
113
113
  const {accept, fileList, maxCount} = this.state;
114
114
 
115
115
  return <Upload
116
- action={SysUtils.contextPath('/admin/sysFile/upload')}
116
+ action={UrlUtils.contextPath('/admin/sysFile/upload')}
117
117
  listType={this.props.listType || 'picture-card'}
118
118
  fileList={fileList}
119
119
  onChange={this.handleChange}
@@ -2,13 +2,11 @@
2
2
  export * from './types';
3
3
  export * from './FieldRemoteSelect';
4
4
  export * from './FieldRemoteSelectMultiple';
5
- export * from './FieldRemoteSelectMultipleInline';
6
5
  export * from './FieldRemoteTree';
7
6
  export * from './FieldDictSelect';
8
7
  export * from './FieldEditor'
9
8
  export * from './FieldRemoteTreeCascader'
10
9
  export * from './FieldRemoteTreeSelect'
11
- export * from './FieldRemoteTreeSelectMultiple'
12
10
  export * from './FieldBoolean';
13
11
  export * from './FieldDate';
14
12
  export * from './FieldDateRange';
@@ -19,4 +17,4 @@ export * from './FieldSysOrgTreeSelect'
19
17
  export * from './FieldSysOrgTree'
20
18
  export * from './FieldPercent'
21
19
  export * from './FieldUploadFile';
22
- export * from '../components/OrgTree';
20
+ export * from '../biz/OrgTree';
@@ -1,4 +1,6 @@
1
+ export * from './biz'
1
2
  export * from './components'
2
3
  export * from './utils'
3
4
  export * from './fields'
4
5
  export * from './views'
6
+ export * from './GlobalData'
@@ -0,0 +1,6 @@
1
+ export class ArrayUtils {
2
+ static contains<T>(arr: T[], item: T): boolean;
3
+ static containsAny<T>(arr: T[], ...items: T[]): boolean;
4
+ static maxBy<T extends Record<K, any>, K extends keyof T>(arr: T[], key: K): T | undefined;
5
+ static unique<T>(arr: T[]): T[];
6
+ }
@@ -0,0 +1,30 @@
1
+ export class ArrayUtils {
2
+ static contains(arr, item) {
3
+ return arr.indexOf(item) !== -1;
4
+ }
5
+
6
+ static containsAny(arr, ...items) {
7
+ for (const item of items) {
8
+ if (ArrayUtils.contains(arr, item)) return true;
9
+ }
10
+ return false;
11
+ }
12
+
13
+ static maxBy(arr, key) {
14
+ if (arr == null || arr.length === 0) return undefined;
15
+ let maxElement = undefined;
16
+ let maxValue = -Infinity;
17
+ for (const element of arr) {
18
+ const value = element[key];
19
+ if (typeof value === 'number' && value > maxValue) {
20
+ maxValue = value;
21
+ maxElement = element;
22
+ }
23
+ }
24
+ return maxElement;
25
+ }
26
+
27
+ static unique(arr) {
28
+ return [...new Set(arr)];
29
+ }
30
+ }
@@ -0,0 +1,20 @@
1
+ export class DateUtils {
2
+ static convertTypeToFormat(type: string): string;
3
+ static year(date: Date): number;
4
+ static month(date: Date): string;
5
+ static date(date: Date): string;
6
+ static hour(date: Date): string;
7
+ static minute(date: Date): string;
8
+ static second(date: Date): string;
9
+ static formatDate(d: Date): string;
10
+ static formatTime(d: Date): string;
11
+ static formatDateTime(d: Date): string;
12
+ static formatDateCn(d: Date): string;
13
+ static now(): string;
14
+ static today(): string;
15
+ static thisYear(): number;
16
+ static thisMonth(): string;
17
+ static friendlyTime(pastDate: Date | string | number): string | undefined;
18
+ static friendlyTotalTime(time: number | string | null): string | null;
19
+ static beginOfMonth(): string;
20
+ }
@@ -0,0 +1,81 @@
1
+ import {StringUtils} from './StringUtils';
2
+
3
+ export class DateUtils {
4
+ static convertTypeToFormat(type) {
5
+ if (type === 'YEAR') type = 'YYYY';
6
+ else if (type === 'YEAR_MONTH') type = 'YYYY-MM';
7
+ else if (type === 'YEAR_QUARTER') type = 'YYYY-QQ';
8
+ else if (type === 'DAY') type = 'YYYY-MM-DD';
9
+ return type;
10
+ }
11
+
12
+ static year(date) { return date.getFullYear(); }
13
+ static month(date) { return StringUtils.pad(date.getMonth() + 1, 2); }
14
+ static date(date) { return StringUtils.pad(date.getDate(), 2); }
15
+ static hour(date) { return StringUtils.pad(date.getHours(), 2); }
16
+ static minute(date) { return StringUtils.pad(date.getMinutes(), 2); }
17
+ static second(date) { return StringUtils.pad(date.getSeconds(), 2); }
18
+
19
+ static formatDate(d) {
20
+ return this.year(d) + '-' + this.month(d) + '-' + this.date(d);
21
+ }
22
+
23
+ static formatTime(d) {
24
+ return this.hour(d) + ':' + this.minute(d) + ':' + this.second(d);
25
+ }
26
+
27
+ static formatDateTime(d) {
28
+ return this.formatDate(d) + ' ' + this.formatTime(d);
29
+ }
30
+
31
+ static formatDateCn(d) {
32
+ return this.year(d) + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日';
33
+ }
34
+
35
+ static now() { return this.formatDateTime(new Date()); }
36
+ static today() { return this.formatDate(new Date()); }
37
+ static thisYear() { return this.year(new Date()); }
38
+ static thisMonth() { return this.month(new Date()); }
39
+
40
+ static friendlyTime(pastDate) {
41
+ if (pastDate == null) return undefined;
42
+ if (!(pastDate instanceof Date)) pastDate = new Date(pastDate);
43
+ const currentDate = new Date();
44
+ let elapsedMilliseconds = currentDate.getTime() - pastDate.getTime();
45
+ const suffix = elapsedMilliseconds > 0 ? '前' : '后';
46
+ elapsedMilliseconds = Math.abs(elapsedMilliseconds);
47
+ const elapsedYears = Math.floor(elapsedMilliseconds / (1000 * 60 * 60 * 24 * 365));
48
+ const elapsedMonths = Math.floor(elapsedMilliseconds / (1000 * 60 * 60 * 24 * 30));
49
+ const elapsedDays = Math.floor(elapsedMilliseconds / (1000 * 60 * 60 * 24));
50
+ const elapsedHours = Math.floor(elapsedMilliseconds / (1000 * 60 * 60));
51
+ const elapsedMinutes = Math.floor(elapsedMilliseconds / (1000 * 60));
52
+ const elapsedSeconds = Math.floor(elapsedMilliseconds / 1000);
53
+ if (elapsedYears >= 1) return `${elapsedYears} 年${suffix}`;
54
+ if (elapsedMonths >= 1) return `${elapsedMonths} 个月${suffix}`;
55
+ if (elapsedDays >= 7) { const weeks = Math.floor(elapsedDays / 7); return `${weeks} 周${suffix}`; }
56
+ if (elapsedDays >= 1) return `${elapsedDays} 天${suffix}`;
57
+ if (elapsedHours >= 1) return `${elapsedHours} 小时${suffix}`;
58
+ if (elapsedMinutes >= 1) return `${elapsedMinutes} 分钟${suffix}`;
59
+ return `${elapsedSeconds} 秒${suffix}`;
60
+ }
61
+
62
+ static friendlyTotalTime(time) {
63
+ if (time == null || time === '-') return null;
64
+ let seconds;
65
+ if (typeof time === 'string') seconds = parseInt(time, 10) / 1000;
66
+ else seconds = time / 1000;
67
+ seconds = Math.floor(seconds);
68
+ if (seconds < 60) return seconds + '秒';
69
+ let min = seconds / 60;
70
+ seconds = seconds % 60;
71
+ min = Math.floor(min);
72
+ seconds = Math.floor(seconds);
73
+ return min + '分' + seconds + '秒';
74
+ }
75
+
76
+ static beginOfMonth() {
77
+ const d = new Date();
78
+ d.setDate(1);
79
+ return this.formatDate(d);
80
+ }
81
+ }
@@ -0,0 +1,3 @@
1
+ export class DeviceUtils {
2
+ static isMobileDevice(): boolean;
3
+ }
@@ -0,0 +1,10 @@
1
+ export class DeviceUtils {
2
+
3
+ static isMobileDevice() {
4
+ const userAgent = navigator?.userAgent || navigator?.vendor || window?.opera || '';
5
+ if (/windows phone/i.test(userAgent)) return true;
6
+ if (/android/i.test(userAgent)) return true;
7
+ if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) return true;
8
+ return false;
9
+ }
10
+ }
@@ -0,0 +1,6 @@
1
+ export class EventBus {
2
+ static on<T extends any[] = []>(name: string, callback: (...args: T) => void, ctx?: any): void;
3
+ static once<T extends any[] = []>(name: string, callback: (...args: T) => void, ctx?: any): void;
4
+ static emit<T extends any[] = []>(name: string, ...args: T): void;
5
+ static off<T extends any[] = []>(name: string, callback?: (...args: T) => void): void;
6
+ }
@@ -0,0 +1,51 @@
1
+ export class EventBus {
2
+ static __stack = {};
3
+
4
+ static on(name, callback, ctx) {
5
+ if (!EventBus.__stack[name]) {
6
+ EventBus.__stack[name] = [];
7
+ }
8
+ EventBus.__stack[name].push({ fn: callback, ctx });
9
+ return () => EventBus.off(name, callback);
10
+ }
11
+
12
+ static once(name, callback, ctx) {
13
+ const unsubscribe = () => EventBus.off(name, callback);
14
+ const listener = (...args) => {
15
+ EventBus.off(name, listener);
16
+ callback.apply(ctx, args);
17
+ };
18
+ listener.__callback = callback;
19
+ EventBus.__stack[name] = EventBus.__stack[name] || [];
20
+ EventBus.__stack[name].push({ fn: listener, ctx });
21
+ return unsubscribe;
22
+ }
23
+
24
+ static emit(name, ...args) {
25
+ const list = EventBus.__stack[name];
26
+ if (list !== undefined) {
27
+ list.slice(0).forEach(entry => {
28
+ entry.fn.apply(entry.ctx, args);
29
+ });
30
+ }
31
+ }
32
+
33
+ static off(name, callback) {
34
+ const list = EventBus.__stack[name];
35
+ if (list === undefined) return;
36
+ if (callback === undefined) {
37
+ delete EventBus.__stack[name];
38
+ return;
39
+ }
40
+ const liveEvents = list.filter(
41
+ entry =>
42
+ entry.fn !== callback &&
43
+ entry.fn.__callback !== callback
44
+ );
45
+ if (liveEvents.length !== 0) {
46
+ EventBus.__stack[name] = liveEvents;
47
+ } else {
48
+ delete EventBus.__stack[name];
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type {ModalFuncProps} from 'antd/es/modal/interface';
3
+
4
+ export class MessageUtils {
5
+ static alert(content: any, config?: Omit<ModalFuncProps, 'content' | 'icon' | 'onOk' | 'onCancel'>): Promise<void>;
6
+ static confirm(content: React.ReactNode, config?: Omit<ModalFuncProps, 'content' | 'icon' | 'onOk' | 'onCancel'>): Promise<boolean>;
7
+ static success(content: string, duration?: number): void;
8
+ static error(content: string, duration?: number): void;
9
+ static destroy(): void;
10
+ }
@@ -0,0 +1,43 @@
1
+ import {message, Modal} from 'antd';
2
+
3
+ export class MessageUtils {
4
+
5
+ static alert(content, config) {
6
+ return new Promise(resolve => {
7
+ Modal.info({
8
+ title: '提示',
9
+ content,
10
+ okText: '确定',
11
+ onOk: () => resolve(),
12
+ icon: null,
13
+ ...config,
14
+ });
15
+ });
16
+ }
17
+
18
+ static confirm(content, config) {
19
+ return new Promise((resolve) => {
20
+ Modal.confirm({
21
+ title: '确认操作',
22
+ content,
23
+ okText: '确定',
24
+ cancelText: '取消',
25
+ onOk: () => resolve(true),
26
+ onCancel: () => resolve(false),
27
+ ...config,
28
+ });
29
+ });
30
+ }
31
+
32
+ static success(content, duration = 3) {
33
+ message.success(content, duration);
34
+ }
35
+
36
+ static error(content, duration = 3) {
37
+ message.error(content, duration);
38
+ }
39
+
40
+ static destroy() {
41
+ message.destroy();
42
+ }
43
+ }
@@ -0,0 +1,14 @@
1
+ import {ObjectUtils} from './ObjectUtils';
2
+
3
+ declare module './ObjectUtils' {
4
+ interface ObjectUtils {
5
+ // Empty - no public methods to declare
6
+ }
7
+ }
8
+
9
+ export class ObjectUtils {
10
+ static copyPropertyIfPresent<TSource extends object, TTarget extends object>(
11
+ source: TSource | null | undefined,
12
+ target: TTarget | null | undefined
13
+ ): void;
14
+ }
@@ -0,0 +1,12 @@
1
+ export class ObjectUtils {
2
+
3
+ static copyPropertyIfPresent(source, target) {
4
+ if (!source || !target || typeof source !== 'object' || typeof target !== 'object') return;
5
+ const keys = Object.keys(target);
6
+ for (const key of keys) {
7
+ if (Object.hasOwn(source, key)) {
8
+ target[key] = source[key];
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ export class StorageUtils {
2
+ static get<T>(key: string, defaultValue?: T | null): T | null;
3
+ static set<T>(key: string, value: T | null | undefined): void;
4
+ }
@@ -6,11 +6,11 @@ export class StorageUtils {
6
6
 
7
7
  /**
8
8
  * 从 localStorage 获取数据。
9
- * @param key 存储的键。
10
- * @param defaultValue 如果找不到键或值,返回的默认值 (默认为 null)。
11
- * @returns 存储的值或默认值。
9
+ * @param {string} key 存储的键。
10
+ * @param {*} [defaultValue=null] 如果找不到键或值,返回的默认值。
11
+ * @returns {*} 存储的值或默认值。
12
12
  */
13
- static get<T>(key: string, defaultValue: T | null = null): T | null {
13
+ static get(key, defaultValue = null) {
14
14
  const vStr = localStorage.getItem(key);
15
15
  if (vStr == null) {
16
16
  return defaultValue;
@@ -19,7 +19,6 @@ export class StorageUtils {
19
19
  try {
20
20
  return JSON.parse(vStr);
21
21
  } catch (error) {
22
- // 如果 JSON 解析失败 (例如,存储的值不是一个包含 createTime 和 data 的有效 JSON 结构)
23
22
  console.error(`Error parsing storage item for key: ${key}`, error);
24
23
  return defaultValue;
25
24
  }
@@ -28,22 +27,19 @@ export class StorageUtils {
28
27
  /**
29
28
  * 将数据存储到 localStorage。
30
29
  * 如果值为 null 或 undefined,则删除该键。
31
- * @param key 存储的键。
32
- * @param value 要存储的值。
30
+ * @param {string} key 存储的键。
31
+ * @param {*} value 要存储的值。
33
32
  */
34
- static set<T>(key: string, value: T | null | undefined): void {
35
- // 如果值为 null 或 undefined,则删除该键
33
+ static set(key, value) {
36
34
  if (value == null) {
37
35
  localStorage.removeItem(key);
38
36
  return;
39
37
  }
40
38
 
41
-
42
39
  try {
43
40
  const dataStr = JSON.stringify(value);
44
41
  localStorage.setItem(key, dataStr);
45
42
  } catch (error) {
46
- // 捕获 JSON.stringify 错误 (例如,如果 value 包含循环引用)
47
43
  console.error(`Error stringify storage value for key: ${key}`, error);
48
44
  }
49
45
  }
@@ -0,0 +1,17 @@
1
+ export class StringUtils {
2
+ static readonly ISO_SPLITTER: string;
3
+ static removePrefix(str: string | null | undefined, ch: string): string;
4
+ static removeSuffix(str: string | null | undefined, ch: string): string;
5
+ static random(length: number): string;
6
+ static contains(str: string | null | undefined, subStr: string): boolean;
7
+ static subAfter(source: string | null | undefined, str: string): string;
8
+ static subAfterLast(source: string | null | undefined, str: string): string;
9
+ static subBefore(str: string | null | undefined, sub: string): string;
10
+ static pad(input: string | number | null | undefined, totalLen: number, padChar?: string): string;
11
+ static getWidth(str: string | null | undefined): number;
12
+ static cutByWidth(str: string, maxWidth: number): string;
13
+ static ellipsis(str: string | null | undefined, len: number, suffix?: string): string;
14
+ static isString(value: any): value is string;
15
+ static split(str: any, sp: string): string[];
16
+ static join(arr: any, sp: string): string;
17
+ }