@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.
- package/package.json +3 -2
- package/src/.umi-production/appData.json +78 -77
- package/src/.umi-production/core/route.tsx +6 -6
- package/src/framework/GlobalData.d.ts +8 -0
- package/src/framework/GlobalData.js +33 -0
- package/src/framework/{components/system → biz}/HasPerm.tsx +1 -1
- package/src/framework/{components/OrgTree/index.tsx → biz/OrgTree.tsx} +12 -2
- package/src/framework/{components/system/ButtonList.tsx → biz/PermActions.tsx} +6 -5
- package/src/framework/{components/RoleTree/index.tsx → biz/RoleTree.tsx} +13 -3
- package/src/framework/{components/system → biz}/index.tsx +4 -2
- package/src/framework/components/ContextMenu/index.tsx +1 -1
- package/src/framework/components/DownloadModal/index.tsx +332 -0
- package/src/framework/components/ErrorBoundary/index.tsx +18 -0
- package/src/framework/components/NamedIcon/{index.jsx → index.tsx} +8 -3
- package/src/framework/components/Page/index.tsx +2 -2
- package/src/framework/components/ProTable/index.tsx +3 -10
- package/src/framework/components/index.ts +2 -6
- package/src/framework/fields/BaseRemoteSelect/index.tsx +2 -3
- package/src/framework/fields/FieldEditor/index.tsx +35 -44
- package/src/framework/fields/FieldNumberRange/index.tsx +2 -2
- package/src/framework/fields/FieldRemoteTree/index.tsx +1 -1
- package/src/framework/fields/FieldTable/index.tsx +20 -22
- package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
- package/src/framework/fields/index.ts +1 -3
- package/src/framework/index.ts +2 -0
- package/src/framework/utils/ArrayUtils.d.ts +6 -0
- package/src/framework/utils/ArrayUtils.js +30 -0
- package/src/framework/utils/DateUtils.d.ts +20 -0
- package/src/framework/utils/DateUtils.js +81 -0
- package/src/framework/utils/DeviceUtils.d.ts +3 -0
- package/src/framework/utils/DeviceUtils.js +10 -0
- package/src/framework/utils/EventBus.d.ts +6 -0
- package/src/framework/utils/EventBus.js +51 -0
- package/src/framework/utils/MessageUtils.d.ts +10 -0
- package/src/framework/utils/MessageUtils.js +43 -0
- package/src/framework/utils/ObjectUtils.d.ts +14 -0
- package/src/framework/utils/ObjectUtils.js +12 -0
- package/src/framework/utils/StorageUtils.d.ts +4 -0
- package/src/framework/utils/{StorageUtils.ts → StorageUtils.js} +7 -11
- package/src/framework/utils/StringUtils.d.ts +17 -0
- package/src/framework/utils/StringUtils.js +107 -0
- package/src/framework/utils/TreeUtils.d.ts +13 -0
- package/src/framework/utils/TreeUtils.js +63 -0
- package/src/framework/utils/UrlUtils.d.ts +6 -0
- package/src/framework/utils/UrlUtils.js +71 -0
- package/src/framework/utils/index.ts +1 -5
- package/src/framework/utils/system/DictUtils.d.ts +20 -0
- package/src/framework/utils/system/DictUtils.js +31 -0
- package/src/framework/utils/system/FormRegistry.d.ts +8 -0
- package/src/framework/utils/system/FormRegistry.js +31 -0
- package/src/framework/utils/system/HttpUtils.d.ts +14 -0
- package/src/framework/utils/system/HttpUtils.js +87 -0
- package/src/framework/utils/system/PageUtils.d.ts +14 -0
- package/src/framework/utils/system/PageUtils.js +59 -0
- package/src/framework/utils/system/PermUtils.d.ts +5 -0
- package/src/framework/utils/system/PermUtils.js +27 -0
- package/src/framework/utils/system/ThemeUtils.d.ts +6 -0
- package/src/framework/utils/system/{ThemeUtils.ts → ThemeUtils.js} +19 -19
- package/src/framework/utils/system/index.ts +1 -2
- package/src/framework/views/ViewFile/index.tsx +1 -2
- package/src/framework/views/ViewImage/index.tsx +3 -3
- package/src/framework/views/{ViewBooleanEnableDisable → ViewSwitch}/index.tsx +1 -1
- package/src/framework/views/index.ts +3 -4
- package/src/layouts/admin/HeaderRight.jsx +32 -44
- package/src/layouts/admin/index.jsx +5 -29
- package/src/layouts/index.jsx +26 -17
- package/src/pages/about.jsx +24 -4
- package/src/pages/{userCenter/ChangePassword.jsx → account/changePassword.jsx} +1 -2
- package/src/pages/{userCenter → account}/index.jsx +2 -6
- package/src/pages/forceUpdatePwd.jsx +89 -0
- package/src/pages/login.jsx +5 -5
- package/src/pages/system/dict/index.jsx +6 -6
- package/src/pages/system/file/index.jsx +4 -4
- package/src/pages/system/job/index.jsx +7 -3
- package/src/pages/system/org/index.jsx +2 -2
- package/src/pages/system/role/index.jsx +4 -4
- package/src/pages/system/role/{perm.jsx → rolePerm.jsx} +2 -2
- package/src/pages/system/user/index.jsx +4 -4
- package/src/framework/components/DownloadFileButton/index.tsx +0 -33
- package/src/framework/components/NamedIcon/index.d.ts +0 -7
- package/src/framework/components/ProTable/utils/index.ts +0 -62
- package/src/framework/fields/FieldRemoteSelectMultipleInline/index.tsx +0 -33
- package/src/framework/fields/FieldRemoteTreeSelectMultiple/index.tsx +0 -43
- package/src/framework/utils/ArrayUtils.ts +0 -186
- package/src/framework/utils/ColorsUtils.ts +0 -378
- package/src/framework/utils/DateUtils.ts +0 -187
- package/src/framework/utils/DeviceUtils.ts +0 -46
- package/src/framework/utils/DomUtils.ts +0 -50
- package/src/framework/utils/EventBusUtils.ts +0 -143
- package/src/framework/utils/Logger.ts +0 -40
- package/src/framework/utils/MessageUtils.tsx +0 -90
- package/src/framework/utils/ObjectUtils.ts +0 -118
- package/src/framework/utils/StringUtils.ts +0 -436
- package/src/framework/utils/TreeUtils.ts +0 -251
- package/src/framework/utils/UrlUtils.ts +0 -152
- package/src/framework/utils/UuidUtils.ts +0 -88
- package/src/framework/utils/ValidateUtils.ts +0 -28
- package/src/framework/utils/system/DictUtils.ts +0 -96
- package/src/framework/utils/system/FormRegistryUtils.ts +0 -76
- package/src/framework/utils/system/HttpUtils.ts +0 -251
- package/src/framework/utils/system/PageUtils.ts +0 -163
- package/src/framework/utils/system/PermUtils.ts +0 -79
- package/src/framework/utils/system/SysUtils.ts +0 -105
- package/src/framework/views/ViewProcessInstanceProgress/index.tsx +0 -98
- package/src/framework/views/ViewProcessInstanceProgressButton/index.tsx +0 -23
- package/src/pages/userCenter/message.jsx +0 -105
- /package/src/framework/{components → views}/ViewRange/index.tsx +0 -0
- /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 {
|
|
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 =
|
|
13
|
-
const jsUrl =
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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={(
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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={
|
|
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 '../
|
|
20
|
+
export * from '../biz/OrgTree';
|
package/src/framework/index.ts
CHANGED
|
@@ -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,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
|
+
}
|
|
@@ -6,11 +6,11 @@ export class StorageUtils {
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* 从 localStorage 获取数据。
|
|
9
|
-
* @param key 存储的键。
|
|
10
|
-
* @param defaultValue
|
|
11
|
-
* @returns 存储的值或默认值。
|
|
9
|
+
* @param {string} key 存储的键。
|
|
10
|
+
* @param {*} [defaultValue=null] 如果找不到键或值,返回的默认值。
|
|
11
|
+
* @returns {*} 存储的值或默认值。
|
|
12
12
|
*/
|
|
13
|
-
static get
|
|
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
|
|
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
|
+
}
|