@jiangood/open-admin 3.0.15 → 3.1.1
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 +15 -2
- package/src/framework/biz/OrgTree.tsx +10 -6
- package/src/framework/biz/PermActions.tsx +5 -3
- package/src/framework/biz/RoleTree.tsx +10 -6
- package/src/framework/biz/index.tsx +1 -0
- package/src/framework/components/ContextMenu/index.tsx +17 -1
- package/src/framework/components/DownloadModal/index.tsx +40 -33
- package/src/framework/components/DrawerForm/index.tsx +108 -0
- package/src/framework/components/FormModal/index.tsx +16 -9
- package/src/framework/components/NamedIcon/index.tsx +2 -2
- package/src/framework/components/OrgSwitcher/index.tsx +6 -7
- package/src/framework/components/PageLoading/index.tsx +3 -3
- package/src/framework/components/ProTable/index.tsx +28 -25
- package/src/framework/components/ValueType/index.tsx +1 -1
- package/src/framework/components/index.ts +1 -0
- package/src/framework/config/themeConfig.ts +5 -5
- package/src/framework/fields/BaseRemoteSelect/index.tsx +23 -21
- package/src/framework/fields/FieldBoolean/index.tsx +6 -6
- package/src/framework/fields/FieldDate/index.tsx +10 -10
- package/src/framework/fields/FieldDateRange/index.tsx +20 -20
- package/src/framework/fields/FieldDictSelect/index.tsx +2 -2
- package/src/framework/fields/FieldEditor/index.tsx +1 -1
- package/src/framework/fields/FieldNumberRange/index.tsx +16 -17
- package/src/framework/fields/FieldPercent/index.tsx +2 -2
- package/src/framework/fields/FieldRemoteSelect/index.tsx +4 -4
- package/src/framework/fields/FieldRemoteTree/index.tsx +1 -1
- package/src/framework/fields/FieldRemoteTreeCascader/index.tsx +1 -1
- package/src/framework/fields/FieldRemoteTreeSelect/index.tsx +1 -1
- package/src/framework/fields/FieldSysOrgTree/index.tsx +1 -1
- package/src/framework/fields/FieldSysOrgTreeSelect/index.tsx +1 -1
- package/src/framework/fields/FieldTable/index.tsx +15 -13
- package/src/framework/fields/FieldTableSelect/index.tsx +5 -5
- package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
- package/src/framework/fields/FieldUploadImage/index.tsx +31 -29
- package/src/framework/fields/types.ts +0 -2
- package/src/framework/router/PageFrame.tsx +1 -1
- package/src/framework/router/matcher.ts +2 -2
- package/src/framework/utils/ArrayUtils.ts +2 -2
- package/src/framework/utils/DateUtils.ts +1 -1
- package/src/framework/utils/{system/DictUtils.ts → DictUtils.ts} +1 -1
- package/src/framework/utils/EventBus.ts +7 -7
- package/src/framework/utils/HttpClient.ts +283 -0
- package/src/framework/utils/{system/PageUtils.ts → PageUtils.ts} +4 -4
- package/src/framework/utils/{system/PermUtils.ts → PermUtils.ts} +3 -3
- package/src/framework/utils/StringUtils.ts +10 -8
- package/src/framework/utils/TreeUtils.ts +3 -3
- package/src/framework/utils/UrlUtils.ts +2 -2
- package/src/framework/utils/index.ts +4 -1
- package/src/framework/views/ViewApproveStatus/index.tsx +1 -2
- package/src/framework/views/ViewBoolean/index.tsx +4 -1
- package/src/framework/views/ViewFile/index.tsx +4 -4
- package/src/framework/views/ViewImage/index.tsx +2 -2
- package/src/framework/views/ViewPassword/index.tsx +3 -2
- package/src/framework/views/ViewText/index.tsx +12 -3
- package/src/framework/views/types.ts +2 -4
- package/src/layouts/admin/AdminTabs.jsx +9 -0
- package/src/layouts/admin/AdminTabs.less +2 -2
- package/src/layouts/admin/HeaderRight.jsx +8 -7
- package/src/layouts/admin/TabLayout.jsx +2 -2
- package/src/layouts/admin/index.jsx +15 -11
- package/src/layouts/admin/index.less +16 -1
- package/src/layouts/index.tsx +24 -18
- package/src/pages/404.jsx +1 -1
- package/src/pages/article/$code.jsx +4 -4
- package/src/pages/index.jsx +1 -1
- package/src/pages/public/login.jsx +34 -27
- package/src/pages/public/test.jsx +1 -1
- package/src/pages/standalone/forceUpdatePwd.jsx +9 -7
- package/src/pages/system/article/index.jsx +9 -8
- package/src/pages/system/dict/index.jsx +19 -16
- package/src/pages/system/file/index.jsx +9 -9
- package/src/pages/system/job/index.jsx +17 -17
- package/src/pages/system/log/index.jsx +5 -7
- package/src/pages/system/org/index.jsx +31 -18
- package/src/pages/system/role/index.jsx +13 -12
- package/src/pages/system/role/rolePerm.jsx +25 -21
- package/src/pages/system/user/index.jsx +35 -33
- package/src/pages/system/user/userPerm.jsx +12 -12
- package/src/pages/userCenter/index.jsx +11 -9
- package/src/pages/userCenter/permView.jsx +7 -7
- package/src/framework/utils/system/HttpUtils.ts +0 -97
- package/src/framework/utils/system/index.ts +0 -4
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import {Button, Form, Table} from 'antd';
|
|
3
3
|
import type {FormInstance, TableProps} from 'antd';
|
|
4
4
|
|
|
5
|
-
import {StringUtils} from "../../utils";
|
|
5
|
+
import {StringUtils, type AjaxBody} from "../../utils";
|
|
6
6
|
|
|
7
7
|
import './index.less'
|
|
8
8
|
|
|
@@ -13,27 +13,32 @@ export interface ProTableActionRef {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/** request 返回结构(Spring Data Page 序列化 + 扩展数据) */
|
|
16
|
-
export interface ProTableRequestResult<T =
|
|
16
|
+
export interface ProTableRequestResult<T = unknown> {
|
|
17
17
|
content: T[];
|
|
18
18
|
totalElements: number | string;
|
|
19
19
|
size: number;
|
|
20
20
|
extData?: {
|
|
21
21
|
summary?: React.ReactNode;
|
|
22
|
-
[key: string]:
|
|
22
|
+
[key: string]: unknown;
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export interface ProTableProps<T =
|
|
27
|
-
/**
|
|
28
|
-
|
|
26
|
+
export interface ProTableProps<T = unknown> {
|
|
27
|
+
/**
|
|
28
|
+
* 数据请求(Promise 式),框架自动注入 page/size/sort 参数。
|
|
29
|
+
* 返回 HttpClient 的 Promise:request = (params) => HttpClient.get(url, params)
|
|
30
|
+
* ProTable 自动取 AjaxResult 的 data(分页结构 {content, totalElements, extData})。
|
|
31
|
+
* 失败时 HttpClient 默认自动弹错,ProTable 内部静默复位 loading。
|
|
32
|
+
*/
|
|
33
|
+
request: (params: Record<string, unknown>) => Promise<AjaxBody<ProTableRequestResult<T>>>;
|
|
29
34
|
/** antd Table 列定义 */
|
|
30
35
|
columns: TableProps<T>['columns'];
|
|
31
36
|
/** 获取表格操作句柄(reload) */
|
|
32
|
-
actionRef?: React.
|
|
37
|
+
actionRef?: React.RefObject<ProTableActionRef | undefined>;
|
|
33
38
|
/** 获取搜索表单实例 */
|
|
34
|
-
formRef?: React.
|
|
39
|
+
formRef?: React.RefObject<FormInstance | undefined>;
|
|
35
40
|
/** 工具栏渲染,参数为当前搜索值与行选择状态 */
|
|
36
|
-
toolBarRender?: (params: Record<string,
|
|
41
|
+
toolBarRender?: (params: Record<string, unknown>, selection: {
|
|
37
42
|
selectedRows: T[];
|
|
38
43
|
selectedRowKeys: React.Key[];
|
|
39
44
|
}) => React.ReactNode;
|
|
@@ -54,12 +59,12 @@ export interface ProTableProps<T = any> {
|
|
|
54
59
|
treeMode?: boolean;
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
interface ProTableState<T =
|
|
62
|
+
interface ProTableState<T = unknown> {
|
|
58
63
|
selectedRowKeys: React.Key[];
|
|
59
64
|
selectedRows: T[];
|
|
60
65
|
tableSize: 'small' | 'middle' | 'large';
|
|
61
66
|
loading: boolean;
|
|
62
|
-
params: Record<string,
|
|
67
|
+
params: Record<string, unknown>;
|
|
63
68
|
dataSource: T[];
|
|
64
69
|
total: number;
|
|
65
70
|
current: number;
|
|
@@ -70,12 +75,12 @@ interface ProTableState<T = any> {
|
|
|
70
75
|
};
|
|
71
76
|
extData: {
|
|
72
77
|
summary?: React.ReactNode;
|
|
73
|
-
[key: string]:
|
|
78
|
+
[key: string]: unknown;
|
|
74
79
|
};
|
|
75
80
|
scrollY: number | string | null;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
export class ProTable<T =
|
|
83
|
+
export class ProTable<T = unknown> extends React.Component<ProTableProps<T>, ProTableState<T>> {
|
|
79
84
|
|
|
80
85
|
|
|
81
86
|
state: ProTableState<T> = {
|
|
@@ -150,23 +155,22 @@ export class ProTable<T = any> extends React.Component<ProTableProps<T>, ProTabl
|
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
|
|
153
|
-
|
|
154
|
-
request(params).then(rs => {
|
|
155
|
-
const {content, totalElements, size,extData} = rs;
|
|
158
|
+
this.setState({loading: true})
|
|
159
|
+
request(params).then((rs: AjaxBody<ProTableRequestResult<T>>) => {
|
|
160
|
+
const {content, totalElements, size, extData} = rs.data;
|
|
156
161
|
|
|
157
|
-
|
|
158
|
-
this.setState({dataSource: content, total: Number(totalElements),pageSize:size})
|
|
162
|
+
this.setState({dataSource: content, total: Number(totalElements), pageSize: size})
|
|
159
163
|
if (extData) {
|
|
160
164
|
this.setState({extData})
|
|
161
165
|
}
|
|
162
166
|
this.updateSelectedRows(content)
|
|
163
|
-
|
|
164
|
-
}).
|
|
167
|
+
this.setState({loading: false})
|
|
168
|
+
}).catch(() => {
|
|
169
|
+
// 错误提示已由 HttpClient 默认弹出,此处仅复位 loading
|
|
165
170
|
this.setState({loading: false})
|
|
166
171
|
})
|
|
167
172
|
}
|
|
168
173
|
|
|
169
|
-
|
|
170
174
|
// 数据重新加载后,更新toolbar需要的已选择数据行
|
|
171
175
|
updateSelectedRows = list => {
|
|
172
176
|
const {rowKey = "id"} = this.props
|
|
@@ -180,7 +184,6 @@ export class ProTable<T = any> extends React.Component<ProTableProps<T>, ProTabl
|
|
|
180
184
|
|
|
181
185
|
render() {
|
|
182
186
|
const {
|
|
183
|
-
actionRef,
|
|
184
187
|
toolBarRender,
|
|
185
188
|
columns,
|
|
186
189
|
rowSelection,
|
|
@@ -215,7 +218,7 @@ export class ProTable<T = any> extends React.Component<ProTableProps<T>, ProTabl
|
|
|
215
218
|
showTotal: (total) => `共 ${total} 条`
|
|
216
219
|
}}
|
|
217
220
|
|
|
218
|
-
onChange={(pagination, filters, sorter
|
|
221
|
+
onChange={(pagination, filters, sorter) => {
|
|
219
222
|
if (this.props.treeMode) {
|
|
220
223
|
this.setState({sorter}, this.loadData)
|
|
221
224
|
} else {
|
|
@@ -298,11 +301,11 @@ export class ProTable<T = any> extends React.Component<ProTableProps<T>, ProTabl
|
|
|
298
301
|
};
|
|
299
302
|
};
|
|
300
303
|
|
|
301
|
-
onSearch = (values: Record<string,
|
|
304
|
+
onSearch = (values: Record<string, unknown>) => {
|
|
302
305
|
this.setState({params: values, current: 1, sorter: {}}, this.loadData)
|
|
303
306
|
}
|
|
304
307
|
|
|
305
|
-
changeFormValues = (values: Record<string,
|
|
308
|
+
changeFormValues = (values: Record<string, unknown>) => { // NOSONAR: ref 暴露给父组件/业务项目调用的公共 API
|
|
306
309
|
if (this.formRef.current) {
|
|
307
310
|
this.formRef.current.resetFields()
|
|
308
311
|
this.formRef.current.setFieldsValue(values)
|
|
@@ -21,18 +21,18 @@ export const DEFAULT_COLORS: Required<ThemeColors> = {
|
|
|
21
21
|
|
|
22
22
|
let overrideColors: Partial<ThemeColors> | null = null;
|
|
23
23
|
let cachedConfig: ThemeConfig | null = null;
|
|
24
|
-
let cachedToken: Record<string,
|
|
24
|
+
let cachedToken: Record<string, unknown> | null = null;
|
|
25
25
|
|
|
26
26
|
function buildConfig(): ThemeConfig {
|
|
27
|
-
const token: Record<string,
|
|
28
|
-
const components: Record<string,
|
|
27
|
+
const token: Record<string, unknown> = { borderRadius: 4, ...DEFAULT_COLORS, ...overrideColors };
|
|
28
|
+
const components: Record<string, unknown> = {
|
|
29
29
|
Layout: { headerBg: 'white', triggerHeight: 32 },
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
return { token, components };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function syncCssVars(token: Record<string,
|
|
35
|
+
function syncCssVars(token: Record<string, unknown>) {
|
|
36
36
|
if (typeof document === 'undefined') return;
|
|
37
37
|
const el = document.documentElement;
|
|
38
38
|
el.style.setProperty('--primary-color', token.colorPrimary);
|
|
@@ -54,7 +54,7 @@ export function getThemeConfig(): ThemeConfig {
|
|
|
54
54
|
return cachedConfig;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
export function getToken(): Record<string,
|
|
57
|
+
export function getToken(): ThemeColors & Record<string, unknown> {
|
|
58
58
|
if (!cachedToken) {
|
|
59
59
|
cachedToken = getDesignToken(getThemeConfig());
|
|
60
60
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Spin } from 'antd';
|
|
3
3
|
import { debounce } from 'lodash';
|
|
4
|
-
import {
|
|
4
|
+
import { HttpClient } from '../../utils';
|
|
5
5
|
|
|
6
6
|
interface BaseRemoteSelectState {
|
|
7
|
-
data:
|
|
7
|
+
data: Record<string, unknown>[];
|
|
8
8
|
loading: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface BaseRemoteSelectProps {
|
|
12
12
|
url?: string;
|
|
13
|
-
value?:
|
|
13
|
+
value?: unknown;
|
|
14
14
|
debounceTime?: number;
|
|
15
|
-
[key: string]:
|
|
15
|
+
[key: string]: unknown;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -21,11 +21,11 @@ export interface BaseRemoteSelectProps {
|
|
|
21
21
|
* 处理通用的数据加载、防抖搜索、竞态处理、错误提示逻辑。
|
|
22
22
|
* 子类只需覆写 getLoadParams() 和 render()。
|
|
23
23
|
*/
|
|
24
|
-
export class BaseRemoteSelect<P extends BaseRemoteSelectProps = BaseRemoteSelectProps> extends React.Component<P, BaseRemoteSelectState> {
|
|
25
|
-
private fetchIdRef: number = 0;
|
|
24
|
+
export class BaseRemoteSelect<P extends BaseRemoteSelectProps = BaseRemoteSelectProps> extends React.Component<P, BaseRemoteSelectState> { // NOSONAR: state/防抖字段构造器赋值
|
|
25
|
+
private fetchIdRef: number = 0; // NOSONAR: 构造器中赋值
|
|
26
26
|
private loadDataDebounce: ReturnType<typeof debounce>;
|
|
27
27
|
|
|
28
|
-
static defaultProps = {
|
|
28
|
+
static readonly defaultProps = {
|
|
29
29
|
debounceTime: 300,
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -35,7 +35,7 @@ export class BaseRemoteSelect<P extends BaseRemoteSelectProps = BaseRemoteSelect
|
|
|
35
35
|
data: [],
|
|
36
36
|
loading: false,
|
|
37
37
|
};
|
|
38
|
-
this.loadDataDebounce = debounce(this._loadData,
|
|
38
|
+
this.loadDataDebounce = debounce(this._loadData, props.debounceTime || 300);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
componentDidMount() {
|
|
@@ -56,7 +56,7 @@ export class BaseRemoteSelect<P extends BaseRemoteSelectProps = BaseRemoteSelect
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/** 返回请求参数。searchText=undefined 表示初始加载 */
|
|
59
|
-
getLoadParams(searchText?: string): Record<string,
|
|
59
|
+
getLoadParams(searchText?: string): Record<string, unknown> {
|
|
60
60
|
return { searchText, selected: this.props.value };
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -67,28 +67,30 @@ export class BaseRemoteSelect<P extends BaseRemoteSelectProps = BaseRemoteSelect
|
|
|
67
67
|
|
|
68
68
|
// ========== 数据加载 ==========
|
|
69
69
|
|
|
70
|
-
private _loadData =
|
|
70
|
+
private _loadData = (searchText?: string) => {
|
|
71
71
|
const url = this.getUrl();
|
|
72
72
|
const fetchId = ++this.fetchIdRef;
|
|
73
73
|
|
|
74
74
|
this.setState({ loading: true });
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
const done = () => {
|
|
77
|
+
if (fetchId === this.fetchIdRef) {
|
|
78
|
+
this.setState({ loading: false });
|
|
79
|
+
}
|
|
80
|
+
};
|
|
78
81
|
|
|
82
|
+
HttpClient.get(url, this.getLoadParams(searchText), {toastError: false}).then((data) => {
|
|
79
83
|
if (fetchId === this.fetchIdRef) {
|
|
80
|
-
this.setState({ data: data || [] });
|
|
84
|
+
this.setState({ data: data.data || [] });
|
|
81
85
|
}
|
|
82
|
-
|
|
86
|
+
done();
|
|
87
|
+
}).catch((error) => {
|
|
83
88
|
console.warn('[BaseRemoteSelect] 加载失败:', error);
|
|
84
89
|
if (fetchId === this.fetchIdRef) {
|
|
85
90
|
this.setState({ data: [] });
|
|
86
91
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
this.setState({ loading: false });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
+
done();
|
|
93
|
+
});
|
|
92
94
|
};
|
|
93
95
|
|
|
94
96
|
/**
|
|
@@ -115,14 +117,14 @@ export class BaseRemoteSelect<P extends BaseRemoteSelectProps = BaseRemoteSelect
|
|
|
115
117
|
|
|
116
118
|
// ========== Helper 方法 ==========
|
|
117
119
|
|
|
118
|
-
getShowSearch(): { filterOption: false; onSearch: (value: string) => void } {
|
|
120
|
+
getShowSearch(): { filterOption: false; onSearch: (value: string) => void } { // NOSONAR: 由子类通过 this.getShowSearch() 调用
|
|
119
121
|
return {
|
|
120
122
|
filterOption: false,
|
|
121
123
|
onSearch: this.handleSearch,
|
|
122
124
|
};
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
getNotFoundContent(): React.ReactNode {
|
|
127
|
+
getNotFoundContent(): React.ReactNode { // NOSONAR: 由子类通过 this.getNotFoundContent() 调用
|
|
126
128
|
return this.state.loading ? <Spin size="small" /> : '数据为空';
|
|
127
129
|
}
|
|
128
130
|
}
|
|
@@ -23,7 +23,7 @@ export interface FieldBooleanProps extends FieldProps<boolean> {
|
|
|
23
23
|
|
|
24
24
|
export class FieldBoolean extends React.Component<FieldBooleanProps> {
|
|
25
25
|
|
|
26
|
-
static defaultProps = {
|
|
26
|
+
static readonly defaultProps = {
|
|
27
27
|
type: 'select'
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -34,7 +34,7 @@ export class FieldBoolean extends React.Component<FieldBooleanProps> {
|
|
|
34
34
|
|
|
35
35
|
switch (type) {
|
|
36
36
|
case 'radio':
|
|
37
|
-
return <Radio.Group value={v} onChange={e => onChange
|
|
37
|
+
return <Radio.Group value={v} onChange={e => onChange?.(e.target.value)}>
|
|
38
38
|
<Radio value={true}>是</Radio>
|
|
39
39
|
<Radio value={false}>否</Radio>
|
|
40
40
|
</Radio.Group>;
|
|
@@ -42,7 +42,7 @@ export class FieldBoolean extends React.Component<FieldBooleanProps> {
|
|
|
42
42
|
return <Checkbox
|
|
43
43
|
checked={v}
|
|
44
44
|
onChange={(e) => {
|
|
45
|
-
onChange
|
|
45
|
+
onChange?.(e.target.checked);
|
|
46
46
|
}}
|
|
47
47
|
/>;
|
|
48
48
|
case 'select':
|
|
@@ -51,7 +51,7 @@ export class FieldBoolean extends React.Component<FieldBooleanProps> {
|
|
|
51
51
|
{value: false, label: '否'},
|
|
52
52
|
]}
|
|
53
53
|
value={v}
|
|
54
|
-
onChange={value => onChange
|
|
54
|
+
onChange={value => onChange?.(value)}
|
|
55
55
|
style={{width: '100%'}}
|
|
56
56
|
allowClear={true}
|
|
57
57
|
placeholder={'请选择'}
|
|
@@ -59,7 +59,7 @@ export class FieldBoolean extends React.Component<FieldBooleanProps> {
|
|
|
59
59
|
case 'switch':
|
|
60
60
|
return <Switch
|
|
61
61
|
checked={v}
|
|
62
|
-
onChange={checked => onChange
|
|
62
|
+
onChange={checked => onChange?.(checked)}
|
|
63
63
|
/>;
|
|
64
64
|
default:
|
|
65
65
|
return null;
|
|
@@ -67,7 +67,7 @@ export class FieldBoolean extends React.Component<FieldBooleanProps> {
|
|
|
67
67
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
parseBoolean(v:
|
|
70
|
+
parseBoolean(v: unknown): boolean | undefined {
|
|
71
71
|
if (v === null || v === undefined) {
|
|
72
72
|
return undefined;
|
|
73
73
|
}
|
|
@@ -17,11 +17,11 @@ export interface FieldDateProps extends FieldProps<string> {
|
|
|
17
17
|
/** 自定义样式 */
|
|
18
18
|
style?: React.CSSProperties;
|
|
19
19
|
/** 其余属性透传给 DatePicker/TimePicker */
|
|
20
|
-
[key: string]:
|
|
20
|
+
[key: string]: unknown;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export class FieldDate extends React.Component<FieldDateProps> {
|
|
24
|
-
static defaultProps = {
|
|
24
|
+
static readonly defaultProps = {
|
|
25
25
|
type: 'YYYY-MM-DD'
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -33,34 +33,34 @@ export class FieldDate extends React.Component<FieldDateProps> {
|
|
|
33
33
|
case 'YYYY':
|
|
34
34
|
return <DatePicker
|
|
35
35
|
value={this.strToDate(value, formattedType)}
|
|
36
|
-
onChange={v => onChange
|
|
36
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY'))}
|
|
37
37
|
picker="year"
|
|
38
38
|
{...rest}
|
|
39
39
|
/>;
|
|
40
40
|
case 'YYYY-MM':
|
|
41
41
|
return <DatePicker
|
|
42
42
|
value={this.strToDate(value, 'YYYY-MM')}
|
|
43
|
-
onChange={v => onChange
|
|
43
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM'))}
|
|
44
44
|
picker="month"
|
|
45
45
|
{...rest}
|
|
46
46
|
/>;
|
|
47
47
|
case 'YYYY-QQ':
|
|
48
48
|
return <DatePicker
|
|
49
49
|
value={this.strToDate(value, 'YYYY-QQ')}
|
|
50
|
-
onChange={v => onChange
|
|
50
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-QQ'))}
|
|
51
51
|
picker="quarter"
|
|
52
52
|
{...rest}
|
|
53
53
|
/>;
|
|
54
54
|
case 'YYYY-MM-DD':
|
|
55
55
|
return <DatePicker
|
|
56
56
|
value={this.strToDate(value, 'YYYY-MM-DD')}
|
|
57
|
-
onChange={v => onChange
|
|
57
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM-DD'))}
|
|
58
58
|
{...rest}
|
|
59
59
|
></DatePicker>;
|
|
60
60
|
case 'YYYY-MM-DD HH:mm':
|
|
61
61
|
return <DatePicker
|
|
62
62
|
value={this.strToDate(value, 'YYYY-MM-DD HH:mm')}
|
|
63
|
-
onChange={v => onChange
|
|
63
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM-DD HH:mm'))}
|
|
64
64
|
format='YYYY-MM-DD HH:mm'
|
|
65
65
|
showTime
|
|
66
66
|
{...rest}
|
|
@@ -68,7 +68,7 @@ export class FieldDate extends React.Component<FieldDateProps> {
|
|
|
68
68
|
case 'YYYY-MM-DD HH:mm:ss':
|
|
69
69
|
return <DatePicker
|
|
70
70
|
value={this.strToDate(value, 'YYYY-MM-DD HH:mm:ss')}
|
|
71
|
-
onChange={v => onChange
|
|
71
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM-DD HH:mm:ss'))}
|
|
72
72
|
showTime
|
|
73
73
|
{...rest}
|
|
74
74
|
></DatePicker>;
|
|
@@ -76,13 +76,13 @@ export class FieldDate extends React.Component<FieldDateProps> {
|
|
|
76
76
|
return <TimePicker
|
|
77
77
|
format='HH:mm'
|
|
78
78
|
value={this.strToDate(value, 'HH:mm')}
|
|
79
|
-
onChange={v => onChange
|
|
79
|
+
onChange={v => onChange?.(this.dateToStr(v, 'HH:mm'))}
|
|
80
80
|
{...rest}
|
|
81
81
|
></TimePicker>;
|
|
82
82
|
case 'HH:mm:ss':
|
|
83
83
|
return <TimePicker
|
|
84
84
|
value={this.strToDate(value, 'HH:mm:ss')}
|
|
85
|
-
onChange={v => onChange
|
|
85
|
+
onChange={v => onChange?.(this.dateToStr(v, 'HH:mm:ss'))}
|
|
86
86
|
{...rest}
|
|
87
87
|
></TimePicker>;
|
|
88
88
|
default:
|
|
@@ -10,7 +10,7 @@ import type {FieldProps} from '../types';
|
|
|
10
10
|
const SP = StringUtils.ISO_SPLITTER;
|
|
11
11
|
|
|
12
12
|
/** 日期范围值,形如 "2024-01-01/2024-01-31"(起止以 / 分隔) */
|
|
13
|
-
export type FieldDateRangeValue = string;
|
|
13
|
+
export type FieldDateRangeValue = string; // NOSONAR: npm 包导出类型,业务项目引用
|
|
14
14
|
|
|
15
15
|
export interface FieldDateRangeProps extends FieldProps<FieldDateRangeValue> {
|
|
16
16
|
/** 日期类型,如 YYYY-MM-DD、YYYY-MM、HH:mm:ss 等,默认 YYYY-MM-DD */
|
|
@@ -22,11 +22,11 @@ export interface FieldDateRangeProps extends FieldProps<FieldDateRangeValue> {
|
|
|
22
22
|
/** 自定义样式 */
|
|
23
23
|
style?: React.CSSProperties;
|
|
24
24
|
/** 其余属性透传给 RangePicker */
|
|
25
|
-
[key: string]:
|
|
25
|
+
[key: string]: unknown;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export class FieldDateRange extends React.Component<FieldDateRangeProps> {
|
|
29
|
-
static defaultProps = {
|
|
29
|
+
static readonly defaultProps = {
|
|
30
30
|
type: 'YYYY-MM-DD'
|
|
31
31
|
};
|
|
32
32
|
|
|
@@ -37,57 +37,57 @@ export class FieldDateRange extends React.Component<FieldDateRangeProps> {
|
|
|
37
37
|
switch (formattedType) {
|
|
38
38
|
case 'YYYY':
|
|
39
39
|
return <DatePicker.RangePicker
|
|
40
|
-
value={this.strToDate(value
|
|
41
|
-
onChange={v => onChange
|
|
40
|
+
value={this.strToDate(value)}
|
|
41
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY'))}
|
|
42
42
|
picker="year"
|
|
43
43
|
{...rest}
|
|
44
44
|
/>;
|
|
45
45
|
case 'YYYY-MM':
|
|
46
46
|
return <DatePicker.RangePicker
|
|
47
|
-
value={this.strToDate(value
|
|
48
|
-
onChange={v => onChange
|
|
47
|
+
value={this.strToDate(value)}
|
|
48
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM'))}
|
|
49
49
|
picker="month"
|
|
50
50
|
{...rest}
|
|
51
51
|
/>;
|
|
52
52
|
case 'YYYY-QQ':
|
|
53
53
|
return <DatePicker.RangePicker
|
|
54
|
-
value={this.strToDate(value
|
|
55
|
-
onChange={v => onChange
|
|
54
|
+
value={this.strToDate(value)}
|
|
55
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-QQ'))}
|
|
56
56
|
picker="quarter"
|
|
57
57
|
{...rest}
|
|
58
58
|
/>;
|
|
59
59
|
case 'YYYY-MM-DD':
|
|
60
60
|
return <DatePicker.RangePicker
|
|
61
|
-
value={this.strToDate(value
|
|
62
|
-
onChange={v => onChange
|
|
61
|
+
value={this.strToDate(value)}
|
|
62
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM-DD'))}
|
|
63
63
|
{...rest}
|
|
64
64
|
/>;
|
|
65
65
|
case 'YYYY-MM-DD HH:mm':
|
|
66
66
|
return <DatePicker.RangePicker
|
|
67
|
-
value={this.strToDate(value
|
|
68
|
-
onChange={v => onChange
|
|
67
|
+
value={this.strToDate(value)}
|
|
68
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM-DD HH:mm'))}
|
|
69
69
|
format='YYYY-MM-DD HH:mm'
|
|
70
70
|
showTime
|
|
71
71
|
{...rest}
|
|
72
72
|
/>;
|
|
73
73
|
case 'YYYY-MM-DD HH:mm:ss':
|
|
74
74
|
return <DatePicker.RangePicker
|
|
75
|
-
value={this.strToDate(value
|
|
76
|
-
onChange={v => onChange
|
|
75
|
+
value={this.strToDate(value)}
|
|
76
|
+
onChange={v => onChange?.(this.dateToStr(v, 'YYYY-MM-DD HH:mm:ss'))}
|
|
77
77
|
showTime
|
|
78
78
|
{...rest}
|
|
79
79
|
/>;
|
|
80
80
|
case 'HH:mm':
|
|
81
81
|
return <TimePicker.RangePicker
|
|
82
82
|
format='HH:mm'
|
|
83
|
-
value={this.strToDate(value
|
|
84
|
-
onChange={v => onChange
|
|
83
|
+
value={this.strToDate(value)}
|
|
84
|
+
onChange={v => onChange?.(this.dateToStr(v, 'HH:mm'))}
|
|
85
85
|
{...rest}
|
|
86
86
|
/>;
|
|
87
87
|
case 'HH:mm:ss':
|
|
88
88
|
return <TimePicker.RangePicker
|
|
89
|
-
value={this.strToDate(value
|
|
90
|
-
onChange={v => onChange
|
|
89
|
+
value={this.strToDate(value)}
|
|
90
|
+
onChange={v => onChange?.(this.dateToStr(v, 'HH:mm:ss'))}
|
|
91
91
|
{...rest}
|
|
92
92
|
/>;
|
|
93
93
|
default:
|
|
@@ -96,7 +96,7 @@ export class FieldDateRange extends React.Component<FieldDateRangeProps> {
|
|
|
96
96
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
strToDate(v: string | null | undefined
|
|
99
|
+
strToDate(v: string | null | undefined): [dayjs.Dayjs, dayjs.Dayjs] | null {
|
|
100
100
|
if (!v) {
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
@@ -4,7 +4,7 @@ import {DictUtils} from "../../utils";
|
|
|
4
4
|
import type {SelectProps} from "antd/es/select";
|
|
5
5
|
import type {FieldProps} from '../types';
|
|
6
6
|
|
|
7
|
-
interface FieldDictSelectProps extends Omit<SelectProps, 'options' | 'children' | 'mode' | 'value' | 'onChange'>, FieldProps<
|
|
7
|
+
interface FieldDictSelectProps extends Omit<SelectProps, 'options' | 'children' | 'mode' | 'value' | 'onChange'>, FieldProps<unknown> {
|
|
8
8
|
typeCode: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -12,7 +12,7 @@ export class FieldDictSelect extends React.Component<FieldDictSelectProps> {
|
|
|
12
12
|
render() {
|
|
13
13
|
const {value, typeCode} = this.props;
|
|
14
14
|
const options = DictUtils.dictOptions(typeCode);
|
|
15
|
-
const strValue = value == null ? null : String(value);
|
|
15
|
+
const strValue = value == null ? null : String(value); // NOSONAR: value 经 String() 显式转换,无隐式字符串化
|
|
16
16
|
|
|
17
17
|
return <Select value={strValue}
|
|
18
18
|
onChange={this.props.onChange}
|
|
@@ -69,7 +69,7 @@ export class FieldEditor extends React.Component<FieldEditorProps, {loading: boo
|
|
|
69
69
|
setup: function (editor) {
|
|
70
70
|
editor.on('OpenWindow', function(e) {
|
|
71
71
|
const dialog = e.dialog;
|
|
72
|
-
if (dialog
|
|
72
|
+
if (dialog?.getData().dimensions) {
|
|
73
73
|
dialog.showTab("upload");
|
|
74
74
|
}
|
|
75
75
|
});
|
|
@@ -6,7 +6,9 @@ import type {FieldProps} from '../types';
|
|
|
6
6
|
const SP = StringUtils.ISO_SPLITTER;
|
|
7
7
|
|
|
8
8
|
/** 数字范围值,形如 "1/100"(起止以 / 分隔) */
|
|
9
|
-
export type FieldNumberRangeValue = string;
|
|
9
|
+
export type FieldNumberRangeValue = string; // NOSONAR: npm 包导出类型,业务项目引用
|
|
10
|
+
|
|
11
|
+
export type RangePartValue = number | string | null;
|
|
10
12
|
|
|
11
13
|
export interface FieldNumberRangeProps extends FieldProps<FieldNumberRangeValue> {
|
|
12
14
|
/** 默认值(形如 "1/100"),value 为空时挂载后回填 */
|
|
@@ -14,23 +16,21 @@ export interface FieldNumberRangeProps extends FieldProps<FieldNumberRangeValue>
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export class FieldNumberRange extends React.Component<FieldNumberRangeProps> {
|
|
17
|
-
onChangeA = (a:
|
|
19
|
+
onChangeA = (a: RangePartValue) => {
|
|
18
20
|
const {b} = this.parse(this.props.value);
|
|
19
|
-
this.props.onChange
|
|
21
|
+
this.props.onChange?.(this.merge(this.toPart(a), this.toPart(b)));
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
onChangeB = (b:
|
|
24
|
+
onChangeB = (b: RangePartValue) => {
|
|
23
25
|
const {a} = this.parse(this.props.value);
|
|
24
|
-
this.props.onChange
|
|
26
|
+
this.props.onChange?.(this.merge(this.toPart(a), this.toPart(b)));
|
|
25
27
|
};
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
b = '';
|
|
33
|
-
}
|
|
29
|
+
private toPart(v: RangePartValue): number | string {
|
|
30
|
+
return v ?? '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
merge(a: number | string, b: number | string): string {
|
|
34
34
|
return a + SP + b;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -45,15 +45,14 @@ export class FieldNumberRange extends React.Component<FieldNumberRangeProps> {
|
|
|
45
45
|
componentDidMount() {
|
|
46
46
|
const {value, defaultValue, onChange} = this.props;
|
|
47
47
|
if (value == null && defaultValue) {
|
|
48
|
-
onChange
|
|
48
|
+
onChange?.(defaultValue);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
render() {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
53
|
+
const {defaultValue} = this.props;
|
|
54
|
+
let {value} = this.props;
|
|
55
|
+
value = value ?? defaultValue;
|
|
57
56
|
const {a, b} = this.parse(value);
|
|
58
57
|
|
|
59
58
|
return <div style={{display: 'flex', alignItems: 'center'}}>
|
|
@@ -12,7 +12,7 @@ export interface FieldPercentProps extends Omit<InputNumberProps, 'value' | 'onC
|
|
|
12
12
|
* 数字的百分数输入框
|
|
13
13
|
*/
|
|
14
14
|
export class FieldPercent extends React.Component<FieldPercentProps> {
|
|
15
|
-
static defaultProps = {
|
|
15
|
+
static readonly defaultProps = {
|
|
16
16
|
precision: 2,
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -28,7 +28,7 @@ export class FieldPercent extends React.Component<FieldPercentProps> {
|
|
|
28
28
|
onChange={v => {
|
|
29
29
|
if (v != null) {
|
|
30
30
|
const percentValue = Number((v / 100).toFixed(precision));
|
|
31
|
-
onChange
|
|
31
|
+
onChange?.(percentValue);
|
|
32
32
|
}
|
|
33
33
|
}}
|
|
34
34
|
{...rest}
|