@jiangood/open-admin 2.5.9 → 3.0.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.
Files changed (142) hide show
  1. package/package.json +20 -8
  2. package/src/framework/GlobalData.ts +85 -0
  3. package/src/framework/biz/OrgTree.tsx +2 -7
  4. package/src/framework/biz/Perm.tsx +11 -0
  5. package/src/framework/biz/PermActions.tsx +18 -15
  6. package/src/framework/biz/index.tsx +34 -19
  7. package/src/framework/components/ContextMenu/index.tsx +105 -78
  8. package/src/framework/components/DownloadModal/index.tsx +15 -17
  9. package/src/framework/components/ErrorBoundary/index.tsx +2 -2
  10. package/src/framework/components/FormModal/index.tsx +76 -0
  11. package/src/framework/components/Gap/index.tsx +1 -1
  12. package/src/framework/components/NamedIcon/index.tsx +55 -9
  13. package/src/framework/components/Page/index.less +0 -1
  14. package/src/framework/components/Page/index.tsx +34 -27
  15. package/src/framework/components/PageLoading/index.less +15 -0
  16. package/src/framework/components/PageLoading/index.tsx +32 -16
  17. package/src/framework/components/ProTable/index.tsx +86 -15
  18. package/src/framework/components/index.ts +1 -0
  19. package/src/framework/config/index.ts +1 -0
  20. package/src/framework/config/themeConfig.ts +39 -0
  21. package/src/framework/fields/FieldBoolean/index.tsx +11 -2
  22. package/src/framework/fields/FieldDate/index.tsx +22 -7
  23. package/src/framework/fields/FieldDateRange/index.tsx +24 -6
  24. package/src/framework/fields/FieldDictSelect/index.tsx +1 -0
  25. package/src/framework/fields/FieldEditor/index.tsx +7 -1
  26. package/src/framework/fields/FieldNumberRange/index.tsx +14 -5
  27. package/src/framework/fields/FieldPercent/index.tsx +16 -5
  28. package/src/framework/fields/FieldRemoteSelect/index.tsx +4 -2
  29. package/src/framework/fields/FieldRemoteTreeSelect/index.tsx +2 -1
  30. package/src/framework/fields/FieldSysOrgTree/index.tsx +6 -1
  31. package/src/framework/fields/FieldSysOrgTreeSelect/index.tsx +6 -1
  32. package/src/framework/fields/FieldTable/index.tsx +35 -6
  33. package/src/framework/fields/FieldTableSelect/index.tsx +18 -2
  34. package/src/framework/fields/FieldUploadFile/index.tsx +69 -28
  35. package/src/framework/fields/index.ts +0 -1
  36. package/src/framework/index.ts +2 -0
  37. package/src/framework/router/Link.tsx +26 -0
  38. package/src/framework/router/PageFrame.tsx +31 -0
  39. package/src/framework/router/history.ts +50 -0
  40. package/src/framework/router/index.ts +4 -0
  41. package/src/framework/router/matcher.ts +58 -0
  42. package/src/framework/utils/{ArrayUtils.js → ArrayUtils.ts} +5 -5
  43. package/src/framework/utils/{DateUtils.js → DateUtils.ts} +19 -19
  44. package/src/framework/utils/{DeviceUtils.js → DeviceUtils.ts} +8 -1
  45. package/src/framework/utils/{EventBus.js → EventBus.ts} +13 -6
  46. package/src/framework/utils/MessageApi.ts +5 -0
  47. package/src/framework/utils/ModalApi.ts +5 -0
  48. package/src/framework/utils/ObjectUtils.ts +15 -0
  49. package/src/framework/utils/{StorageUtils.js → StorageUtils.ts} +7 -7
  50. package/src/framework/utils/{StringUtils.js → StringUtils.ts} +15 -15
  51. package/src/framework/utils/{TreeUtils.js → TreeUtils.ts} +19 -12
  52. package/src/framework/utils/{UrlUtils.js → UrlUtils.ts} +10 -10
  53. package/src/framework/utils/index.ts +2 -1
  54. package/src/framework/utils/system/{DictUtils.js → DictUtils.ts} +16 -4
  55. package/src/framework/utils/system/{HttpUtils.js → HttpUtils.ts} +25 -21
  56. package/src/framework/utils/system/PageUtils.ts +49 -0
  57. package/src/framework/utils/system/{PermUtils.js → PermUtils.ts} +5 -6
  58. package/src/framework/utils/system/index.ts +0 -2
  59. package/src/framework/views/ViewBoolean/index.tsx +6 -3
  60. package/src/framework/views/ViewFileButton/index.tsx +13 -8
  61. package/src/framework/views/ViewImage/index.tsx +19 -14
  62. package/src/framework/views/ViewRange/index.tsx +10 -7
  63. package/src/framework/views/ViewText/index.tsx +9 -11
  64. package/src/layouts/admin/AdminTabs.jsx +79 -0
  65. package/src/layouts/admin/AdminTabs.less +63 -0
  66. package/src/layouts/admin/HeaderRight.jsx +27 -13
  67. package/src/layouts/admin/TabLayout.jsx +124 -0
  68. package/src/layouts/admin/index.jsx +5 -6
  69. package/src/layouts/admin/index.less +1 -82
  70. package/src/layouts/index.tsx +148 -0
  71. package/src/main.jsx +8 -0
  72. package/src/pages/account/changePassword.jsx +14 -10
  73. package/src/pages/article/$code.jsx +2 -2
  74. package/src/pages/index.jsx +6 -4
  75. package/src/pages/{login.jsx → public/login.jsx} +7 -10
  76. package/src/pages/public/test.jsx +30 -0
  77. package/src/pages/{forceUpdatePwd.jsx → standalone/forceUpdatePwd.jsx} +3 -24
  78. package/src/pages/system/article/index.jsx +51 -56
  79. package/src/pages/system/dict/index.jsx +60 -91
  80. package/src/pages/system/file/index.jsx +23 -17
  81. package/src/pages/system/job/index.jsx +63 -72
  82. package/src/pages/system/log/index.jsx +11 -8
  83. package/src/pages/system/org/index.jsx +60 -80
  84. package/src/pages/system/role/index.jsx +50 -86
  85. package/src/pages/system/role/rolePerm.jsx +2 -2
  86. package/src/pages/system/user/index.jsx +120 -73
  87. package/src/pages/system/user/userPerm.jsx +2 -2
  88. package/vite-plugin/index.js +107 -0
  89. package/config/common-plugin.js +0 -145
  90. package/config/config.js +0 -5
  91. package/config/utils.js +0 -73
  92. package/src/.umi-production/appData.json +0 -2257
  93. package/src/.umi-production/core/EmptyRoute.tsx +0 -9
  94. package/src/.umi-production/core/defineApp.ts +0 -16
  95. package/src/.umi-production/core/helmet.ts +0 -10
  96. package/src/.umi-production/core/helmetContext.ts +0 -4
  97. package/src/.umi-production/core/history.ts +0 -72
  98. package/src/.umi-production/core/historyIntelli.ts +0 -132
  99. package/src/.umi-production/core/plugin.ts +0 -40
  100. package/src/.umi-production/core/pluginConfig.ts +0 -330
  101. package/src/.umi-production/core/pluginConfigJoi.d.ts +0 -7
  102. package/src/.umi-production/core/polyfill.ts +0 -220
  103. package/src/.umi-production/core/route.tsx +0 -47
  104. package/src/.umi-production/core/routeProps.js +0 -5
  105. package/src/.umi-production/core/routeProps.ts +0 -6
  106. package/src/.umi-production/core/terminal.ts +0 -37
  107. package/src/.umi-production/exports.ts +0 -17
  108. package/src/.umi-production/testBrowser.tsx +0 -90
  109. package/src/.umi-production/tsconfig.json +0 -43
  110. package/src/.umi-production/typings.d.ts +0 -136
  111. package/src/.umi-production/umi.ts +0 -83
  112. package/src/framework/GlobalData.d.ts +0 -10
  113. package/src/framework/GlobalData.js +0 -42
  114. package/src/framework/biz/HasPerm.tsx +0 -14
  115. package/src/framework/fields/FieldRemoteSelectMultiple/index.tsx +0 -29
  116. package/src/framework/utils/ArrayUtils.d.ts +0 -6
  117. package/src/framework/utils/DateUtils.d.ts +0 -20
  118. package/src/framework/utils/DeviceUtils.d.ts +0 -3
  119. package/src/framework/utils/EventBus.d.ts +0 -6
  120. package/src/framework/utils/MessageUtils.d.ts +0 -10
  121. package/src/framework/utils/MessageUtils.js +0 -43
  122. package/src/framework/utils/ObjectUtils.d.ts +0 -14
  123. package/src/framework/utils/ObjectUtils.js +0 -12
  124. package/src/framework/utils/StorageUtils.d.ts +0 -4
  125. package/src/framework/utils/StringUtils.d.ts +0 -17
  126. package/src/framework/utils/TreeUtils.d.ts +0 -13
  127. package/src/framework/utils/UrlUtils.d.ts +0 -6
  128. package/src/framework/utils/system/DictUtils.d.ts +0 -20
  129. package/src/framework/utils/system/FormRegistry.d.ts +0 -8
  130. package/src/framework/utils/system/FormRegistry.js +0 -31
  131. package/src/framework/utils/system/HttpUtils.d.ts +0 -14
  132. package/src/framework/utils/system/PageUtils.d.ts +0 -14
  133. package/src/framework/utils/system/PageUtils.js +0 -59
  134. package/src/framework/utils/system/PermUtils.d.ts +0 -5
  135. package/src/framework/utils/system/ThemeUtils.d.ts +0 -6
  136. package/src/framework/utils/system/ThemeUtils.js +0 -52
  137. package/src/layouts/PageRender.tsx +0 -29
  138. package/src/layouts/admin/TabPageRender.jsx +0 -210
  139. package/src/layouts/index.jsx +0 -120
  140. package/src/loading.jsx +0 -19
  141. package/src/pages/about.jsx +0 -16
  142. /package/src/pages/{login.less → public/login.less} +0 -0
@@ -38,5 +38,5 @@ export const Gap: React.FC<GapProps> = ({
38
38
  ...rest.style,
39
39
  };
40
40
 
41
- return <div style={style} {...rest} />;
41
+ return <div {...rest} style={style} />;
42
42
  };
@@ -1,6 +1,25 @@
1
- import * as Icons from '@ant-design/icons';
2
- import { WarningOutlined } from '@ant-design/icons';
3
- import React from 'react';
1
+ import {WarningOutlined} from '@ant-design/icons';
2
+ import React, {useEffect, useState} from 'react';
3
+
4
+ type IconComponent = React.ComponentType<React.ComponentProps<typeof WarningOutlined>>;
5
+
6
+ /**
7
+ * 构建期生成 "图标名 -> 按需加载函数" 的映射。
8
+ * 避免 import * as Icons 全量打包 @ant-design/icons(800+ 图标),
9
+ * 每个图标编译为独立 chunk,仅在菜单/数据引用时按需加载。
10
+ */
11
+ const iconLoaders: Record<string, () => Promise<{ default: IconComponent }>> = {};
12
+ for (const [path, loader] of Object.entries(
13
+ import.meta.glob('/node_modules/@ant-design/icons/es/icons/*.js')
14
+ )) {
15
+ const name = /([^/]+)\.js$/.exec(path)?.[1];
16
+ if (name) {
17
+ iconLoaders[name] = loader as () => Promise<{ default: IconComponent }>;
18
+ }
19
+ }
20
+
21
+ /** 已加载图标缓存,命中后渲染为同步 */
22
+ const iconCache = new Map<string, IconComponent>();
4
23
 
5
24
  type NamedIconProps = {
6
25
  name: string;
@@ -8,12 +27,39 @@ type NamedIconProps = {
8
27
 
9
28
  export function NamedIcon(props: NamedIconProps): React.ReactElement {
10
29
  const {name, ...rest} = props;
11
- const IconType = Icons[name as keyof typeof Icons];
30
+ const [loaded, setLoaded] = useState<{ name: string; comp: IconComponent } | null>(null);
12
31
 
13
- if (IconType) {
14
- return <IconType {...rest}></IconType>;
15
- }
32
+ useEffect(() => {
33
+ if (iconCache.has(name)) {
34
+ return;
35
+ }
36
+ const loader = iconLoaders[name];
37
+ if (!loader) {
38
+ console.warn(`NamedIcon: icon "${name}" not found, using fallback`);
39
+ return;
40
+ }
41
+ let cancelled = false;
42
+ loader().then(mod => {
43
+ iconCache.set(name, mod.default);
44
+ if (!cancelled) {
45
+ setLoaded({name, comp: mod.default});
46
+ }
47
+ }).catch(e => {
48
+ console.warn(`NamedIcon: failed to load icon "${name}"`, e);
49
+ });
50
+ return () => {
51
+ cancelled = true;
52
+ };
53
+ }, [name]);
54
+
55
+ const Icon = (loaded && loaded.name === name) ? loaded.comp : iconCache.get(name);
16
56
 
17
- console.warn(`NamedIcon: icon "${name}" not found, using fallback`);
18
- return <WarningOutlined {...rest} />;
57
+ if (Icon) {
58
+ return <Icon {...rest} />;
59
+ }
60
+ if (!iconLoaders[name]) {
61
+ return <WarningOutlined {...rest} />;
62
+ }
63
+ // 加载中:1em 占位,避免菜单布局抖动
64
+ return <span style={{display: 'inline-block', width: '1em', height: '1em', verticalAlign: '-0.125em'}}/>;
19
65
  }
@@ -2,7 +2,6 @@
2
2
  width: 100%;
3
3
  height: 100%;
4
4
  overflow: auto;
5
- background-color: #f5f7fa;
6
5
 
7
6
  .oa-page-header {
8
7
  margin-bottom: 16px;
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { Typography } from "antd";
3
3
  import './index.less'
4
- import {ThemeUtils} from "../../utils";
4
+ import {getToken} from "../../config";
5
5
  import { PermActions } from "../../biz/PermActions";
6
6
 
7
7
  interface PageProps {
@@ -14,34 +14,41 @@ interface PageProps {
14
14
  children?: React.ReactNode;
15
15
  }
16
16
 
17
- export const Page: React.FC<PageProps> = ({ padding = true, backgroundGray = false, debug = false, title, description, actions, children }) => {
18
- const hasHeader = title != null;
19
- const style: React.CSSProperties = {};
17
+ export class Page extends React.Component<PageProps> {
18
+ render() {
19
+ const { padding = true, backgroundGray = false, debug = false, title, description, actions, children } = this.props;
20
+ const hasHeader = title != null || description != null || actions != null;
21
+ const style: React.CSSProperties = {};
20
22
 
21
- if (padding) {
22
- style.padding = 16;
23
- }
24
- if (backgroundGray) {
25
- style.backgroundColor = ThemeUtils.getColor("background-color");
26
- }
27
- if (debug) {
28
- style.backgroundColor = 'rgba(255, 0, 0, 0.08)';
29
- }
23
+ if (padding) {
24
+ style.padding = 16;
25
+ }
26
+ const token = getToken();
27
+ if (backgroundGray) {
28
+ style.backgroundColor = token.colorBgLayout;
29
+ }
30
+ if (debug) {
31
+ style.backgroundColor = 'rgba(255, 0, 0, 0.08)';
32
+ }
33
+ if (!style.backgroundColor) {
34
+ style.backgroundColor = token.colorBgLayout;
35
+ }
30
36
 
31
- return <div className={'oa-page'} style={style}>
32
- {hasHeader && (
33
- <div className="oa-page-header">
34
- <div className="oa-page-header-top">
35
- <div className="oa-page-header-left">
36
- <div>
37
- {title && <Typography.Title level={5} style={{ margin: 0 }}>{title}</Typography.Title>}
38
- {description && <div><Typography.Text type="secondary">{description}</Typography.Text></div>}
37
+ return <div className={'oa-page'} style={style}>
38
+ {hasHeader && (
39
+ <div className="oa-page-header">
40
+ <div className="oa-page-header-top">
41
+ <div className="oa-page-header-left">
42
+ <div>
43
+ {title && <Typography.Title level={5} style={{ margin: 0 }}>{title}</Typography.Title>}
44
+ {description && <div><Typography.Text type="secondary">{description}</Typography.Text></div>}
45
+ </div>
39
46
  </div>
47
+ {actions && <PermActions>{actions}</PermActions>}
40
48
  </div>
41
- {actions && <PermActions>{actions}</PermActions>}
42
49
  </div>
43
- </div>
44
- )}
45
- {children}
46
- </div>
47
- };
50
+ )}
51
+ {children}
52
+ </div>;
53
+ }
54
+ }
@@ -0,0 +1,15 @@
1
+ .oa-page-loading {
2
+ height: 100vh;
3
+ width: 100%;
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+
8
+ .oa-page-loading-content {
9
+ text-align: center;
10
+
11
+ .oa-page-loading-messages {
12
+ margin-top: 24px;
13
+ }
14
+ }
15
+ }
@@ -1,24 +1,40 @@
1
1
  import React from "react";
2
2
  import {Alert, Spin} from "antd";
3
- import {ThemeUtils} from "../../utils";
3
+ import {LoadingOutlined} from "@ant-design/icons";
4
+ import {getToken} from "../../config";
5
+ import './index.less'
4
6
 
5
- export function PageLoading(props: { message?: string; messages?: string[] }) {
7
+ export interface PageLoadingProps {
8
+ message?: string;
9
+ messages?: string[];
10
+ }
11
+
12
+ export function PageLoading(props: PageLoadingProps) {
6
13
  const titles = props.messages || (props.message ? [props.message] : ['页面加载中...']);
14
+ const primaryColor = getToken().colorPrimary;
7
15
 
8
- return <div style={{
9
- height: '100vh', width: '100%',
10
- display: 'flex', alignItems: 'center', justifyContent: "center",
11
- color: ThemeUtils.getColor("primary-color")
12
- }}>
13
- <div style={{textAlign: "center", marginTop: '-10rem'}}>
14
- <div>
15
- <Spin size={"large"}></Spin>
16
- </div>
17
- <div style={{marginTop: '1rem'}}>
18
- {titles.map((msg, i) => (
19
- <Alert key={i} message={msg} type="info" showIcon style={{marginBottom: i < titles.length - 1 ? 8 : 0}} />
20
- ))}
16
+ return (
17
+ <div className="oa-page-loading">
18
+ <div className="oa-page-loading-content">
19
+ <div>
20
+ <Spin indicator={<LoadingOutlined style={{fontSize: 48, color: primaryColor}} spin/>}/>
21
+ </div>
22
+ <div className="oa-page-loading-messages">
23
+ {titles.map((msg, i) => (
24
+ <Alert
25
+ key={i}
26
+ title={<span style={{color: primaryColor}}>{msg}</span>}
27
+ type="info"
28
+ showIcon
29
+ style={{
30
+ marginBottom: i < titles.length - 1 ? 8 : 0,
31
+ border: `1px solid ${primaryColor}20`,
32
+ background: `${primaryColor}08`,
33
+ }}
34
+ />
35
+ ))}
36
+ </div>
21
37
  </div>
22
38
  </div>
23
- </div>;
39
+ );
24
40
  }
@@ -1,15 +1,81 @@
1
1
  import React from "react";
2
2
  import {Button, Form, Table} from 'antd';
3
+ import type {FormInstance, TableProps} from 'antd';
3
4
 
4
5
  import {StringUtils} from "../../utils";
5
6
 
6
7
  import './index.less'
7
8
 
9
+ /** 通过 actionRef 暴露的表格操作 */
10
+ export interface ProTableActionRef {
11
+ reload: () => void;
12
+ }
13
+
14
+ /** request 返回结构(Spring Data Page 序列化 + 扩展数据) */
15
+ export interface ProTableRequestResult<T = any> {
16
+ content: T[];
17
+ totalElements: number | string;
18
+ size: number;
19
+ extData?: {
20
+ summary?: React.ReactNode;
21
+ [key: string]: any;
22
+ };
23
+ }
24
+
25
+ export interface ProTableProps<T = any> {
26
+ /** 数据请求,框架自动注入 page/size/sort 参数 */
27
+ request: (params: Record<string, any>) => Promise<ProTableRequestResult<T>>;
28
+ /** antd Table 列定义 */
29
+ columns: TableProps<T>['columns'];
30
+ /** 获取表格操作句柄(reload) */
31
+ actionRef?: React.MutableRefObject<ProTableActionRef | undefined>;
32
+ /** 获取搜索表单实例 */
33
+ formRef?: React.MutableRefObject<FormInstance | undefined>;
34
+ /** 工具栏渲染,参数为当前搜索值与行选择状态 */
35
+ toolBarRender?: (params: Record<string, any>, selection: {
36
+ selectedRows: T[];
37
+ selectedRowKeys: React.Key[];
38
+ }) => React.ReactNode;
39
+ rowKey?: string;
40
+ /** 行选择:true 为默认 checkbox,对象可覆盖 type/onChange */
41
+ rowSelection?: boolean | {
42
+ type?: 'checkbox' | 'radio';
43
+ onChange?: (selectedRowKeys: React.Key[], selectedRows: T[]) => void;
44
+ };
45
+ defaultPageSize?: number;
46
+ /** 搜索栏每行列数,默认 4 */
47
+ searchFormCols?: number;
48
+ /** 搜索表单渲染函数,返回 Form.Item 列表 */
49
+ searchFormRender?: () => React.ReactNode;
50
+ scrollY?: number | string;
51
+ bordered?: boolean;
52
+ }
8
53
 
9
- export class ProTable extends React.Component {
54
+ interface ProTableState<T = any> {
55
+ selectedRowKeys: React.Key[];
56
+ selectedRows: T[];
57
+ tableSize: 'small' | 'middle' | 'large';
58
+ loading: boolean;
59
+ params: Record<string, any>;
60
+ dataSource: T[];
61
+ total: number;
62
+ current: number;
63
+ pageSize: number;
64
+ sorter: {
65
+ field?: string;
66
+ order?: 'ascend' | 'descend' | null;
67
+ };
68
+ extData: {
69
+ summary?: React.ReactNode;
70
+ [key: string]: any;
71
+ };
72
+ scrollY: number | string | null;
73
+ }
74
+
75
+ export class ProTable<T = any> extends React.Component<ProTableProps<T>, ProTableState<T>> {
10
76
 
11
77
 
12
- state = {
78
+ state: ProTableState<T> = {
13
79
  selectedRowKeys: [],
14
80
  selectedRows: [],
15
81
 
@@ -39,7 +105,10 @@ export class ProTable extends React.Component {
39
105
  }
40
106
 
41
107
 
42
- constructor(props) {
108
+ id: string;
109
+ formRef: React.RefObject<FormInstance | null> = React.createRef();
110
+
111
+ constructor(props: ProTableProps<T>) {
43
112
  super(props);
44
113
  if (props.defaultPageSize) {
45
114
  this.state.pageSize = props.defaultPageSize
@@ -47,8 +116,6 @@ export class ProTable extends React.Component {
47
116
  this.id = StringUtils.random(32)
48
117
  }
49
118
 
50
- formRef = React.createRef()
51
-
52
119
  componentDidMount() {
53
120
  this.loadData()
54
121
  if (this.props.actionRef) {
@@ -72,7 +139,7 @@ export class ProTable extends React.Component {
72
139
  const {sorter} = this.state
73
140
 
74
141
  const {field, order} = sorter
75
- if (field) {
142
+ if (field && order) {
76
143
  params.sort = field + "," + (order === 'ascend' ? 'asc' : 'desc')
77
144
  }
78
145
 
@@ -82,7 +149,7 @@ export class ProTable extends React.Component {
82
149
  const {content, totalElements, size,extData} = rs;
83
150
 
84
151
 
85
- this.setState({dataSource: content, total: parseInt(totalElements),pageSize:size})
152
+ this.setState({dataSource: content, total: Number(totalElements),pageSize:size})
86
153
  if (extData) {
87
154
  this.setState({extData})
88
155
  }
@@ -160,19 +227,23 @@ export class ProTable extends React.Component {
160
227
 
161
228
 
162
229
  renderForm = () => {
163
- if (!this.props.children) return
230
+ if (this.props.children) {
231
+ throw new Error('[ProTable] children 已废弃,请使用 searchFormRender prop 替代')
232
+ }
233
+ const searchContent = this.props.searchFormRender?.()
234
+ if (!searchContent) return
164
235
 
165
236
  return (
166
237
  <Form
167
238
  className="filter-bar"
168
- style={{gridTemplateColumns: `repeat(${this.props.searchColumns ?? 4}, 1fr)`}}
239
+ style={{gridTemplateColumns: `repeat(${this.props.searchFormCols ?? 4}, 1fr)`}}
169
240
  onFinish={(values) => this.onSearch(values)}
170
241
  ref={(instance) => {
171
242
  this.formRef.current = instance;
172
243
  if (this.props.formRef) this.props.formRef.current = instance;
173
244
  }}
174
245
  >
175
- {this.props.children}
246
+ {searchContent}
176
247
  <div className="filter-actions">
177
248
  <Button type='primary' htmlType="submit">查询</Button>
178
249
  <Button onClick={() => {
@@ -184,7 +255,7 @@ export class ProTable extends React.Component {
184
255
  )
185
256
  };
186
257
 
187
- getToolBarRenderNode(toolBarRender) {
258
+ getToolBarRenderNode(toolBarRender: NonNullable<ProTableProps<T>['toolBarRender']>) {
188
259
  if (!toolBarRender) {
189
260
  return
190
261
  }
@@ -195,7 +266,7 @@ export class ProTable extends React.Component {
195
266
  });
196
267
  }
197
268
 
198
- getRowSelectionProps = rowSelection => {
269
+ getRowSelectionProps = (rowSelection: ProTableProps<T>['rowSelection']) => {
199
270
  if (rowSelection == null || rowSelection === false) {
200
271
  return null
201
272
  }
@@ -217,11 +288,11 @@ export class ProTable extends React.Component {
217
288
  };
218
289
  };
219
290
 
220
- onSearch = (values) => {
221
- this.setState({params: values, current: 1}, this.loadData)
291
+ onSearch = (values: Record<string, any>) => {
292
+ this.setState({params: values, current: 1, sorter: {}}, this.loadData)
222
293
  }
223
294
 
224
- changeFormValues = (values) => {
295
+ changeFormValues = (values: Record<string, any>) => {
225
296
  if (this.formRef.current) {
226
297
  this.formRef.current.resetFields()
227
298
  this.formRef.current.setFieldsValue(values)
@@ -8,3 +8,4 @@ export * from './ValueType'
8
8
  export * from './ContextMenu'
9
9
  export * from './DownloadModal'
10
10
  export { default as ErrorBoundary } from './ErrorBoundary'
11
+ export { default as FormModal } from './FormModal'
@@ -0,0 +1 @@
1
+ export { getThemeConfig, getToken } from './themeConfig'
@@ -0,0 +1,39 @@
1
+ import { theme } from 'antd';
2
+ import type { ThemeConfig } from 'antd';
3
+
4
+ const { getDesignToken } = theme;
5
+
6
+ let cachedConfig: ThemeConfig | null = null;
7
+ let cachedToken: Record<string, any> | null = null;
8
+
9
+ function buildConfig(): ThemeConfig {
10
+ const token: Record<string, any> = { borderRadius: 4 };
11
+ const components: Record<string, any> = {
12
+ Layout: { headerBg: 'white', triggerHeight: 32 },
13
+ };
14
+
15
+ try {
16
+ const env = import.meta.env;
17
+ if (env.VITE_THEME_PRIMARY_COLOR) token.colorPrimary = env.VITE_THEME_PRIMARY_COLOR;
18
+ if (env.VITE_THEME_SUCCESS_COLOR) token.colorSuccess = env.VITE_THEME_SUCCESS_COLOR;
19
+ if (env.VITE_THEME_WARNING_COLOR) token.colorWarning = env.VITE_THEME_WARNING_COLOR;
20
+ if (env.VITE_THEME_ERROR_COLOR) token.colorError = env.VITE_THEME_ERROR_COLOR;
21
+ if (env.VITE_THEME_BACKGROUND_COLOR) token.colorBgLayout = env.VITE_THEME_BACKGROUND_COLOR;
22
+ } catch (e) {}
23
+
24
+ return { token, components };
25
+ }
26
+
27
+ export function getThemeConfig(): ThemeConfig {
28
+ if (!cachedConfig) {
29
+ cachedConfig = buildConfig();
30
+ }
31
+ return cachedConfig;
32
+ }
33
+
34
+ export function getToken(): Record<string, any> {
35
+ if (!cachedToken) {
36
+ cachedToken = getDesignToken(getThemeConfig());
37
+ }
38
+ return cachedToken;
39
+ }
@@ -11,8 +11,17 @@
11
11
  */
12
12
  import React from 'react';
13
13
  import {Checkbox, Radio, Select, Switch} from 'antd';
14
+ import type {FieldProps} from '../types';
14
15
 
15
- export class FieldBoolean extends React.Component {
16
+ /** 布尔值的展示类型 */
17
+ export type FieldBooleanType = 'select' | 'radio' | 'checkbox' | 'switch';
18
+
19
+ export interface FieldBooleanProps extends FieldProps<boolean> {
20
+ /** 展示类型,默认 select */
21
+ type?: FieldBooleanType;
22
+ }
23
+
24
+ export class FieldBoolean extends React.Component<FieldBooleanProps> {
16
25
 
17
26
  static defaultProps = {
18
27
  type: 'select'
@@ -58,7 +67,7 @@ export class FieldBoolean extends React.Component {
58
67
 
59
68
  }
60
69
 
61
- parseBoolean(v) {
70
+ parseBoolean(v: any): boolean | undefined {
62
71
  if (v === null || v === undefined) {
63
72
  return undefined;
64
73
  }
@@ -5,15 +5,29 @@ import React from "react";
5
5
  import dayjs from "dayjs";
6
6
  import {DatePicker, TimePicker} from "antd";
7
7
  import {DateUtils} from "../../utils";
8
+ import type {FieldProps} from '../types';
8
9
 
9
- export class FieldDate extends React.Component {
10
+ export interface FieldDateProps extends FieldProps<string> {
11
+ /** 日期类型,如 YYYY-MM-DD、YYYY-MM、HH:mm:ss 等,默认 YYYY-MM-DD */
12
+ type?: string;
13
+ /** 占位文本(透传给 DatePicker/TimePicker) */
14
+ placeholder?: string;
15
+ /** 是否禁用 */
16
+ disabled?: boolean;
17
+ /** 自定义样式 */
18
+ style?: React.CSSProperties;
19
+ /** 其余属性透传给 DatePicker/TimePicker */
20
+ [key: string]: any;
21
+ }
22
+
23
+ export class FieldDate extends React.Component<FieldDateProps> {
10
24
  static defaultProps = {
11
25
  type: 'YYYY-MM-DD'
12
26
  };
13
27
 
14
28
  render() {
15
29
  const {type, value, onChange, ...rest} = this.props;
16
- const formattedType = DateUtils.convertTypeToFormat(type);
30
+ const formattedType = DateUtils.convertTypeToFormat(type as string);
17
31
 
18
32
  switch (formattedType) {
19
33
  case 'YYYY':
@@ -77,19 +91,20 @@ export class FieldDate extends React.Component {
77
91
 
78
92
  }
79
93
 
80
- strToDate(value, fmt) {
94
+ strToDate(value: string | number | dayjs.Dayjs | null | undefined, fmt: string): dayjs.Dayjs | null | undefined {
81
95
  if (value != null && value !== '') {
82
96
  const type = typeof value;
83
97
  if (type === 'string' || type === 'number') {
84
- return dayjs(value, fmt);
98
+ return dayjs(value as string | number, fmt);
85
99
  }
86
100
  }
87
101
 
88
- return value;
102
+ return value as dayjs.Dayjs | null | undefined;
89
103
  }
90
104
 
91
- dateToStr(date, fmt) {
92
- return date ? date.format(fmt) : null;
105
+ dateToStr(date: dayjs.Dayjs | null | undefined, fmt: string): string {
106
+ // 清空时 date 为 null,此时按 null 回调给父组件
107
+ return date ? date.format(fmt) : (null as unknown as string);
93
108
  }
94
109
 
95
110
  }
@@ -5,17 +5,34 @@ import React from "react";
5
5
  import dayjs from "dayjs";
6
6
  import {DatePicker, TimePicker} from "antd";
7
7
  import {DateUtils, StringUtils} from "../../utils";
8
+ import type {FieldProps} from '../types';
8
9
 
9
10
  const SP = StringUtils.ISO_SPLITTER;
10
11
 
11
- export class FieldDateRange extends React.Component {
12
+ /** 日期范围值,形如 "2024-01-01/2024-01-31"(起止以 / 分隔) */
13
+ export type FieldDateRangeValue = string;
14
+
15
+ export interface FieldDateRangeProps extends FieldProps<FieldDateRangeValue> {
16
+ /** 日期类型,如 YYYY-MM-DD、YYYY-MM、HH:mm:ss 等,默认 YYYY-MM-DD */
17
+ type?: string;
18
+ /** 占位文本(透传给 RangePicker) */
19
+ placeholder?: string | [string, string];
20
+ /** 是否禁用 */
21
+ disabled?: boolean | [boolean, boolean];
22
+ /** 自定义样式 */
23
+ style?: React.CSSProperties;
24
+ /** 其余属性透传给 RangePicker */
25
+ [key: string]: any;
26
+ }
27
+
28
+ export class FieldDateRange extends React.Component<FieldDateRangeProps> {
12
29
  static defaultProps = {
13
30
  type: 'YYYY-MM-DD'
14
31
  };
15
32
 
16
33
  render() {
17
34
  const {type, value, onChange, ...rest} = this.props;
18
- const formattedType = DateUtils.convertTypeToFormat(type);
35
+ const formattedType = DateUtils.convertTypeToFormat(type as string);
19
36
 
20
37
  switch (formattedType) {
21
38
  case 'YYYY':
@@ -79,7 +96,7 @@ export class FieldDateRange extends React.Component {
79
96
 
80
97
  }
81
98
 
82
- strToDate(v, fmt) {
99
+ strToDate(v: string | null | undefined, fmt: string): [dayjs.Dayjs, dayjs.Dayjs] | null {
83
100
  if (!v) {
84
101
  return null;
85
102
  }
@@ -90,9 +107,10 @@ export class FieldDateRange extends React.Component {
90
107
  return [dayjs(s1), dayjs(s2)];
91
108
  }
92
109
 
93
- dateToStr(dateArr, fmt) {
94
- const d1 = dateArr[0];
95
- const d2 = dateArr[1];
110
+ dateToStr(dateArr: [dayjs.Dayjs | null, dayjs.Dayjs | null] | null, fmt: string): string {
111
+ const arr = dateArr as [dayjs.Dayjs | null, dayjs.Dayjs | null];
112
+ const d1 = arr[0];
113
+ const d2 = arr[1];
96
114
 
97
115
  const s1 = d1 ? d1.format(fmt) : "";
98
116
  const s2 = d2 ? d2.format(fmt) : "";
@@ -18,6 +18,7 @@ export class FieldDictSelect extends React.Component<FieldDictSelectProps> {
18
18
  onChange={this.props.onChange}
19
19
  style={{width: '100%', minWidth: 200}}
20
20
  options={options}
21
+ notFoundContent={options.length === 0 ? '暂无字典数据' : undefined}
21
22
  />;
22
23
  }
23
24
  }
@@ -1,13 +1,19 @@
1
1
  import React from 'react';
2
2
  import {Editor as TinyMceEditor} from '@tinymce/tinymce-react';
3
3
  import {UrlUtils} from '../../utils';
4
+ import type {FieldProps} from '../types';
5
+
6
+ export interface FieldEditorProps extends FieldProps<string> {
7
+ /** 编辑器高度,默认 300 */
8
+ height?: number;
9
+ }
4
10
 
5
11
  /**
6
12
  * 富文本编辑器
7
13
  *
8
14
  * 图片上传相关配置 https://www.tiny.cloud/docs/tinymce/7/image/
9
15
  */
10
- export class FieldEditor extends React.Component {
16
+ export class FieldEditor extends React.Component<FieldEditorProps> {
11
17
  render() {
12
18
  const uploadUrl = UrlUtils.contextPath('/admin/sysFile/upload');
13
19
  const jsUrl = UrlUtils.contextPath('/admin/tinymce/tinymce.min.js');