@jiangood/open-admin 2.0.1 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/config/common-plugin.js +51 -0
  2. package/config/config.js +1 -56
  3. package/package.json +5 -2
  4. package/src/.umi-production/appData.json +73 -100
  5. package/src/.umi-production/umi.ts +2 -2
  6. package/src/framework/components/DownloadFileButton/index.tsx +1 -1
  7. package/src/framework/components/OrgTree/index.tsx +1 -1
  8. package/src/framework/components/Page/index.less +1 -1
  9. package/src/framework/components/Page/index.tsx +19 -22
  10. package/src/framework/components/PageLoading/index.tsx +5 -3
  11. package/src/framework/components/ProModal/index.tsx +2 -2
  12. package/src/framework/components/ProTable/components/ToolBar/index.tsx +1 -1
  13. package/src/framework/fields/FieldEditor/index.tsx +3 -2
  14. package/src/framework/fields/FieldRemoteTreeSelect/index.tsx +3 -3
  15. package/src/framework/fields/FieldRemoteTreeSelectMultiple/index.tsx +3 -3
  16. package/src/framework/fields/FieldUploadFile/index.tsx +8 -4
  17. package/src/framework/utils/MessageUtils.tsx +21 -99
  18. package/src/framework/utils/system/HttpUtils.ts +5 -1
  19. package/src/framework/utils/system/SysUtils.ts +8 -0
  20. package/src/framework/utils/system/ThemeUtils.ts +37 -12
  21. package/src/framework/views/ViewFile/index.tsx +1 -1
  22. package/src/framework/views/ViewProcessInstanceProgress/index.tsx +1 -0
  23. package/src/layouts/PageRender.tsx +29 -0
  24. package/src/layouts/admin/HeaderRight.jsx +1 -0
  25. package/src/layouts/admin/TabPageRender.jsx +138 -158
  26. package/src/layouts/admin/index.jsx +123 -19
  27. package/src/layouts/admin/index.less +124 -6
  28. package/src/layouts/index.jsx +94 -161
  29. package/src/pages/404.jsx +7 -5
  30. package/src/pages/about.jsx +3 -3
  31. package/src/pages/index.jsx +4 -5
  32. package/src/pages/login.jsx +18 -9
  33. package/src/pages/login.less +0 -1
  34. package/src/pages/system/dict/index.jsx +8 -11
  35. package/src/pages/system/file/index.jsx +6 -6
  36. package/src/pages/system/job/index.jsx +26 -11
  37. package/src/pages/system/log/index.jsx +4 -5
  38. package/src/pages/system/manual/index.jsx +8 -6
  39. package/src/pages/system/org/index.jsx +4 -2
  40. package/src/pages/system/role/index.jsx +16 -14
  41. package/src/pages/system/role/perm.jsx +3 -4
  42. package/src/pages/system/user/UserPerm.jsx +1 -1
  43. package/src/pages/system/user/index.jsx +9 -7
  44. package/src/pages/userCenter/index.jsx +2 -2
  45. package/src/pages/userCenter/message.jsx +4 -2
  46. package/src/layouts/PageRender.d.ts +0 -22
  47. package/src/layouts/PageRender.jsx +0 -90
@@ -11,7 +11,7 @@ import Loading from '/home/runner/work/open-admin/open-admin/web/src/loading.jsx
11
11
  import { ApplyPluginsType } from 'umi';
12
12
  import {FormRegistryUtils} from '/home/runner/work/open-admin/open-admin/web/src/framework';
13
13
 
14
- const publicPath = "/";
14
+ const publicPath = "./";
15
15
  const runtimePublicPath = false;
16
16
 
17
17
  async function render() {
@@ -78,6 +78,6 @@ render();
78
78
 
79
79
  if (typeof window !== 'undefined') {
80
80
  window.g_umi = {
81
- version: '4.6.55',
81
+ version: '4.6.57',
82
82
  };
83
83
  }
@@ -25,7 +25,7 @@ export class DownloadFileButton extends React.Component {
25
25
 
26
26
  return <> <Button {...rest}>{children}</Button>
27
27
 
28
- <Modal title='提示' footer={null} onCancel={()=>{this.setState({open:false})}} maskClosable={false}>
28
+ <Modal title='提示' footer={null} mask={{closable: false}} onCancel={()=>{this.setState({open:false})}}>
29
29
  <Spin /> 下载文件中,请勿关闭浏览器
30
30
  </Modal>
31
31
  </>
@@ -29,7 +29,7 @@ export class OrgTree extends React.Component<any, any> {
29
29
  }
30
30
 
31
31
  if (treeData.length === 0) {
32
- return <Alert type={"warning"} message={'组织机构数据为空'}/>
32
+ return <Alert type={"warning"} title={'组织机构数据为空'}/>
33
33
  }
34
34
 
35
35
  return <Tree
@@ -2,7 +2,7 @@
2
2
  width: 100%;
3
3
 
4
4
  // header 48 footer 43, tab 36
5
- height: calc(100vh - 127px);
5
+ height: 100%;
6
6
 
7
7
  overflow: auto;
8
8
 
@@ -2,29 +2,26 @@ import React from "react";
2
2
  import './index.less'
3
3
  import {ThemeUtils} from "../../utils";
4
4
 
5
- /**
6
- * 上下间隔
7
- */
8
- export class Page extends React.Component {
5
+ interface PageProps {
6
+ padding?: boolean;
7
+ backgroundGray?: boolean;
8
+ debug?: boolean;
9
+ children?: React.ReactNode;
10
+ }
9
11
 
10
- static defaultProps = {
11
- padding: false,
12
- backgroundGray:false
12
+ export const Page: React.FC<PageProps> = ({ padding = true, backgroundGray = false, debug = false, children }) => {
13
+ const style: React.CSSProperties = {};
14
+ if (padding) {
15
+ style.padding = 16;
13
16
  }
14
-
15
- render() {
16
- const style = {}
17
- if(this.props.padding){
18
- style.padding = 16
19
- }
20
- if(this.props.backgroundGray){
21
- style.backgroundColor = ThemeUtils.getColor("background-color")
22
- }
23
-
24
-
25
- return <div className={'tmgg-page'} style={style}>
26
- {this.props.children}
27
- </div>
17
+ if (backgroundGray) {
18
+ style.backgroundColor = ThemeUtils.getColor("background-color");
19
+ }
20
+ if (debug) {
21
+ style.backgroundColor = 'rgba(255, 0, 0, 0.08)';
28
22
  }
29
23
 
30
- }
24
+ return <div className={'tmgg-page'} style={style}>
25
+ {children}
26
+ </div>
27
+ };
@@ -2,8 +2,8 @@ import React from "react";
2
2
  import {Alert, Spin} from "antd";
3
3
  import {ThemeUtils} from "../../utils";
4
4
 
5
- export function PageLoading(props: { message?: string }) {
6
- const msg = props.message || '页面加载中...';
5
+ export function PageLoading(props: { message?: string; messages?: string[] }) {
6
+ const titles = props.messages || (props.message ? [props.message] : ['页面加载中...']);
7
7
 
8
8
  return <div style={{
9
9
  height: '100vh', width: '100%',
@@ -15,7 +15,9 @@ export function PageLoading(props: { message?: string }) {
15
15
  <Spin size={"large"}></Spin>
16
16
  </div>
17
17
  <div style={{marginTop: '1rem'}}>
18
- <Alert title={msg}></Alert>
18
+ {titles.map((msg, i) => (
19
+ <Alert key={i} message={msg} type="info" showIcon style={{marginBottom: i < titles.length - 1 ? 8 : 0}} />
20
+ ))}
19
21
  </div>
20
22
  </div>
21
23
  </div>;
@@ -51,8 +51,8 @@ export class ProModal extends React.Component<ProModalProps, any> {
51
51
  render() {
52
52
  return (
53
53
  <Modal
54
- maskClosable={false}
55
- destroyOnClose
54
+ mask={{ closable: false }}
55
+ destroyOnHidden
56
56
  title={this.props.title}
57
57
  open={this.state.visible}
58
58
  onCancel={this.hide}
@@ -55,7 +55,7 @@ export class Toolbar extends React.Component {
55
55
  open={this.state.historyOpen}
56
56
  onCancel={() => this.setState({historyOpen: false})}
57
57
  footer={null}
58
- destroyOnClose
58
+ destroyOnHidden
59
59
  >
60
60
 
61
61
  <Table
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import {Editor as TinyMceEditor} from '@tinymce/tinymce-react';
3
+ import {SysUtils} from '../../utils';
3
4
 
4
5
  /**
5
6
  * 富文本编辑器
@@ -8,8 +9,8 @@ import {Editor as TinyMceEditor} from '@tinymce/tinymce-react';
8
9
  */
9
10
  export class FieldEditor extends React.Component {
10
11
  render() {
11
- const uploadUrl = 'admin/sysFile/upload';
12
- const jsUrl = 'admin/tinymce/tinymce.min.js';
12
+ const uploadUrl = SysUtils.contextPath('/admin/sysFile/upload');
13
+ const jsUrl = SysUtils.contextPath('/admin/tinymce/tinymce.min.js');
13
14
  const {value, onChange, height} = this.props;
14
15
 
15
16
  return (
@@ -23,14 +23,14 @@ export class FieldRemoteTreeSelect extends BaseRemoteSelect {
23
23
  <TreeSelect
24
24
  style={{ width: '100%', minWidth: 200 }}
25
25
  allowClear
26
- dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
26
+ styles={{ popup: { root: { maxHeight: 400, overflow: 'auto' } } }}
27
27
  treeData={data}
28
28
  showCheckedStrategy={TreeSelect.SHOW_ALL}
29
29
  value={value || undefined}
30
30
  onChange={onChange}
31
- filterTreeNode={(inputValue, treeNode) =>
31
+ showSearch={{ filterTreeNode: (inputValue, treeNode) =>
32
32
  StringUtils.contains(treeNode.title, inputValue)
33
- }
33
+ }}
34
34
  treeLine={{ showLeafIcon: true }}
35
35
  treeDefaultExpandAll={treeDefaultExpandAll}
36
36
  />
@@ -26,15 +26,15 @@ export class FieldRemoteTreeSelectMultiple extends BaseRemoteSelect {
26
26
  <TreeSelect
27
27
  style={style}
28
28
  allowClear
29
- dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
29
+ styles={{ popup: { root: { maxHeight: 400, overflow: 'auto' } } }}
30
30
  treeData={data}
31
31
  showCheckedStrategy={TreeSelect.SHOW_ALL}
32
32
  value={value || undefined}
33
33
  onChange={onChange}
34
34
  multiple
35
- filterTreeNode={(inputValue, treeNode) =>
35
+ showSearch={{ filterTreeNode: (inputValue, treeNode) =>
36
36
  StringUtils.contains(treeNode.title, inputValue)
37
- }
37
+ }}
38
38
  treeLine={{ showLeafIcon: true }}
39
39
  treeDefaultExpandAll={treeDefaultExpandAll}
40
40
  />
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
  import ImgCrop from "antd-img-crop";
3
- import {Modal, Upload} from "antd";
3
+ import {Modal, Upload, message} from "antd";
4
4
  import { UploadOutlined } from "@ant-design/icons";
5
5
  import {ViewFile} from "../../views";
6
- import {ObjectUtils} from "../../utils";
6
+ import {ObjectUtils, SysUtils} 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 = 'admin/sysFile/preview/' + id;
36
+ const url = SysUtils.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
  }
@@ -73,6 +73,10 @@ export class FieldUploadFile extends React.Component {
73
73
  return;
74
74
  }
75
75
 
76
+ if (file.status === 'done' && rs?.success) {
77
+ message.success(`文件「${rs.data?.name || ''}」上传成功`);
78
+ }
79
+
76
80
  this.setState({fileList});
77
81
 
78
82
  const newIds = this.convertComponentValueToOutput(fileList);
@@ -109,7 +113,7 @@ export class FieldUploadFile extends React.Component {
109
113
  const {accept, fileList, maxCount} = this.state;
110
114
 
111
115
  return <Upload
112
- action={'admin/sysFile/upload'}
116
+ action={SysUtils.contextPath('/admin/sysFile/upload')}
113
117
  listType={this.props.listType || 'picture-card'}
114
118
  fileList={fileList}
115
119
  onChange={this.handleChange}
@@ -1,44 +1,29 @@
1
1
  import {Form, Input, InputNumber, message, Modal} from 'antd';
2
2
  import type {ModalFuncProps} from 'antd/es/modal/interface';
3
3
  import React from 'react';
4
- import {MessageInstance} from "antd/lib/message/interface";
5
- import {HookAPI} from "antd/lib/modal/useModal";
6
- import {Logger} from "./Logger";
7
-
8
-
9
4
 
10
5
  /**
11
- * 消息工具类 (MsgUtils)
12
- * 封装了 Ant Design 的 Modal, message, 和 notification 静态方法
6
+ * 消息工具类
7
+ * 直接使用 Ant Design 6 的静态方法,无需 hook 初始化
13
8
  */
14
9
  export class MessageUtils {
15
10
 
16
- /**
17
- * 弹出 Alert 提示框
18
- */
19
11
  static alert(content: any, config?: Omit<ModalFuncProps, 'content' | 'icon' | 'onOk' | 'onCancel'>) {
20
12
  return new Promise(resolve => {
21
- this.modalApi.info({
13
+ Modal.info({
22
14
  title: '提示',
23
15
  content,
24
16
  okText: '确定',
25
- onOk: (close)=>{
26
- close()
27
- resolve()
28
- },
17
+ onOk: () => resolve(),
29
18
  icon: null,
30
19
  ...config,
31
20
  });
32
21
  })
33
-
34
22
  }
35
23
 
36
- /**
37
- * 弹出 Confirm 确认框
38
- */
39
24
  static confirm(content: React.ReactNode, config?: Omit<ModalFuncProps, 'content' | 'icon' | 'onOk' | 'onCancel'>) {
40
25
  return new Promise((resolve) => {
41
- this.modalApi.confirm({
26
+ Modal.confirm({
42
27
  title: '确认操作',
43
28
  content,
44
29
  okText: '确定',
@@ -48,29 +33,25 @@ export class MessageUtils {
48
33
  ...config,
49
34
  });
50
35
  })
51
-
52
36
  }
53
37
 
54
- /**
55
- * 弹出 Prompt 输入框对话框, 如果默认值是数字, 则使用 InputNumber 输入框
56
- */
57
- static prompt(message: React.ReactNode, initialValue?: string|number, placeholder?: string, config?: Omit<ModalFuncProps, 'content' | 'title' | 'icon' | 'onOk'>) {
38
+ static prompt(msg: React.ReactNode, initialValue?: string | number, placeholder?: string, config?: Omit<ModalFuncProps, 'content' | 'title' | 'icon' | 'onOk'>) {
58
39
  const isNumber = typeof initialValue === 'number';
59
40
  return new Promise((resolve) => {
60
41
  const ref = React.createRef()
61
- let element:any = isNumber ? <InputNumber placeholder={placeholder}/> : <Input placeholder={placeholder}/>;
62
- const content:any = <div>
63
- <div style={{marginBottom: 4}}>{message}</div>
42
+ let element: any = isNumber ? <InputNumber placeholder={placeholder}/> : <Input placeholder={placeholder}/>;
43
+ const content: any = <div>
44
+ <div style={{marginBottom: 4}}>{msg}</div>
64
45
  <Form ref={ref}>
65
46
  <Form.Item name='inputValue' initialValue={initialValue}>
66
47
  {element}
67
48
  </Form.Item>
68
49
  </Form>
69
50
  </div>;
70
- this.modalApi.confirm({
51
+ Modal.confirm({
71
52
  icon: null,
72
53
  title: '提示',
73
- content:content,
54
+ content,
74
55
  okText: '确定',
75
56
  cancelText: '取消',
76
57
  onOk: () => {
@@ -86,83 +67,24 @@ export class MessageUtils {
86
67
  })
87
68
  }
88
69
 
89
- // --- Antd message 封装 (全局通知/Loading) ---
90
-
91
- /**
92
- * 成功消息
93
- */
94
- static success(content: String, duration: number = 3) {
95
- if(!this.messageApi){
96
- alert(content)
97
- return
98
- }
99
- this.messageApi.success(content, duration);
70
+ static success(content: string, duration: number = 3) {
71
+ message.success(content, duration);
100
72
  }
101
73
 
102
- /**
103
- * 错误消息
104
- */
105
- static error(content: String, duration: number = 3) {
106
- if(!this.messageApi){
107
- alert(content)
108
- return
109
- }
110
- this.messageApi.error(content, duration);
74
+ static error(content: string, duration: number = 3) {
75
+ message.error(content, duration);
111
76
  }
112
77
 
113
- /**
114
- * 警告消息
115
- */
116
- static warning(content: String, duration: number = 3) {
117
- if(!this.messageApi){
118
- alert(content)
119
- return
120
- }
121
- this.messageApi.warning(content, duration);
78
+ static warning(content: string, duration: number = 3) {
79
+ message.warning(content, duration);
122
80
  }
123
81
 
124
- /**
125
- * 通用消息
126
- */
127
82
  static info(content: React.ReactNode, duration: number = 3) {
128
- if(!this.messageApi){
129
- alert(content)
130
- return
131
- }
132
- this.messageApi.info(content, duration);
83
+ message.info(content, duration);
133
84
  }
134
85
 
135
- /**
136
- * 弹出 Loading 提示
137
- */
138
86
  static loading(content: string = '正在加载...', duration?: number) {
139
- duration = duration === undefined ? 0 : duration;
140
- return this.messageApi.loading(content, duration);
87
+ duration = duration === undefined ? 0 : duration;
88
+ return message.loading(content, duration);
141
89
  }
142
-
143
- static config(messageApi: MessageInstance, modalApi: HookAPI) {
144
- this.messageApi = messageApi;
145
- this.modalApi = modalApi;
146
- }
147
-
148
- private static modalApi:HookAPI = null;
149
- private static messageApi:MessageInstance = null;
150
- }
151
-
152
- /**
153
- * antd6 推荐使用这个hooks,这里统一设置, 供公共layout使用
154
- * @constructor
155
- */
156
- export function MessageHolder(props){
157
- const [modalApi, modalContextHolder] = Modal.useModal();
158
- const [messageApi, messageContextHolder] = message.useMessage();
159
-
160
- React.useEffect(()=>{
161
- Logger.getLogger("MessageHolder").debug('Rendered')
162
- MessageUtils.config(messageApi,modalApi);
163
- props.onFinish()
164
- },[])
165
- return <>
166
- {modalContextHolder} {messageContextHolder}
167
- </>
168
- }
90
+ }
@@ -6,6 +6,7 @@ import {MessageUtils} from "../MessageUtils";
6
6
 
7
7
 
8
8
  const axiosInstance = axios.create({
9
+ baseURL: (typeof SERVLET_CONTEXT !== 'undefined' && SERVLET_CONTEXT) || '',
9
10
  withCredentials: true,
10
11
  headers: {
11
12
  'Content-Type': 'application/json'
@@ -35,7 +36,7 @@ export class HttpUtils {
35
36
  config.url = url.startsWith('admin') ? '/' + url : url;
36
37
 
37
38
  return new Promise((resolve, reject) => {
38
- axios(config).then((response: AxiosResponse) => {
39
+ axiosInstance(config).then((response: AxiosResponse) => {
39
40
  const body = response.data;
40
41
  // 假设后端响应结构为 { success: boolean | null, message: string, data: any, code: number }
41
42
  let {success, message, data} = body;
@@ -45,6 +46,7 @@ export class HttpUtils {
45
46
  }
46
47
 
47
48
  if (!success) {
49
+ console.error(`[HttpUtils] 请求失败: ${url}`, {code: body.code, message, data: body});
48
50
  MessageUtils.error(message || '操作失败');
49
51
  reject('操作失败');
50
52
  return
@@ -57,6 +59,7 @@ export class HttpUtils {
57
59
  resolve(transformData ? data : response);
58
60
  }).catch((e: unknown) => {
59
61
  // 统一异常处理
62
+ console.error(`[HttpUtils] 请求异常: ${url}`, e);
60
63
  let msg = '操作失败';
61
64
 
62
65
  if (axios.isAxiosError(e)) {
@@ -238,6 +241,7 @@ export class HttpUtils {
238
241
  const response = await HttpUtils.coreRequest(downloadConfig, false) as AxiosResponse;
239
242
  await HttpUtils.handleDownloadBlob(response);
240
243
  } catch (error) {
244
+ console.error('[HttpUtils] 下载文件失败:', error);
241
245
  // coreRequest已经处理了401、504和通用的错误提示
242
246
  return Promise.reject(error);
243
247
  }
@@ -89,6 +89,14 @@ export class SysUtils {
89
89
  public static getDictInfo(): DictInfo {
90
90
  return StorageUtils.get(DICT_INFO_KEY);
91
91
  }
92
+
93
+ /**
94
+ * 拼接 servlet context-path,例如 contextPath('/admin/auth/login') → '/open-admin/admin/auth/login'
95
+ */
96
+ public static contextPath(path: string): string {
97
+ const base = (typeof SERVLET_CONTEXT !== 'undefined' && SERVLET_CONTEXT) || '';
98
+ return base + path;
99
+ }
92
100
  }
93
101
 
94
102
  // 导出常量(保持不变,但通常将常量放在类内部作为 static readonly 属性更符合 class 模式,
@@ -1,25 +1,50 @@
1
- /**
2
- * 主题相关的工具类
3
- */
1
+ import {ColorsUtils} from '../ColorsUtils';
2
+
3
+ declare const OPEN_ADMIN_THEME: Record<string, string> | undefined;
4
+
4
5
  export class ThemeUtils {
5
- // 使用 readonly 属性来定义主题常量,保持其不可变性
6
- public static readonly theme = {
6
+ private static readonly defaultTheme: Record<string, string> = {
7
7
  "primary-color": "#1961AC",
8
8
  "success-color": "#52c41a",
9
9
  "warning-color": "#faad14",
10
10
  "error-color": "#ff4d4f",
11
11
  "background-color": "#f5f5f5",
12
-
13
12
  "primary-color-hover": "#4990CD",
14
13
  "primary-color-click": "#124B93"
15
14
  };
16
15
 
17
- /**
18
- * @description 可选:提供一个获取主题颜色的静态方法,增强工具类的可用性
19
- * @param key 主题颜色键名
20
- * @returns 对应的颜色值,如果键名不存在则返回undefined
21
- */
22
- public static getColor(key: keyof typeof ThemeUtils.theme): string | undefined {
16
+ private static cachedTheme: Record<string, string> | null = null;
17
+
18
+ static get theme(): Record<string, string> {
19
+ if (this.cachedTheme) return this.cachedTheme;
20
+ this.cachedTheme = this.loadTheme();
21
+ return this.cachedTheme;
22
+ }
23
+
24
+ private static loadTheme(): Record<string, string> {
25
+ let custom: Record<string, string> = {};
26
+ try {
27
+ if (typeof OPEN_ADMIN_THEME !== 'undefined' && OPEN_ADMIN_THEME) {
28
+ custom = OPEN_ADMIN_THEME;
29
+ }
30
+ } catch {}
31
+
32
+ const result = {...this.defaultTheme, ...custom};
33
+
34
+ // Auto-derive hover/click from primary if not explicitly set
35
+ if (custom["primary-color"]) {
36
+ if (!custom["primary-color-hover"]) {
37
+ result["primary-color-hover"] = ColorsUtils.lighten(result["primary-color"], 20);
38
+ }
39
+ if (!custom["primary-color-click"]) {
40
+ result["primary-color-click"] = ColorsUtils.lighten(result["primary-color"], -10);
41
+ }
42
+ }
43
+
44
+ return result;
45
+ }
46
+
47
+ public static getColor(key: string): string | undefined {
23
48
  return ThemeUtils.theme[key];
24
49
  }
25
50
  }
@@ -41,7 +41,7 @@ export class ViewFile extends React.Component {
41
41
  });
42
42
 
43
43
  return <div style={{height}}>
44
- <Carousel dotPosition={"top"}>
44
+ <Carousel dotPlacement={"top"}>
45
45
  {iframeList}
46
46
  </Carousel>
47
47
  </div>
@@ -32,6 +32,7 @@ export class ViewProcessInstanceProgress extends React.Component {
32
32
  this.setState(rs)
33
33
  this.setState({data: rs})
34
34
  }).catch(e => {
35
+ console.error('[ViewProcessInstanceProgress] 加载流程实例失败:', e);
35
36
  this.setState({errorMsg: e})
36
37
  }).finally(() => {
37
38
  this.setState({loading: false})
@@ -0,0 +1,29 @@
1
+ import {matchRoutes, useAppData} from "umi";
2
+ import {Result} from "antd";
3
+
4
+ interface PageRenderProps {
5
+ pathname: string;
6
+ }
7
+
8
+ /**
9
+ * 通过指定 pathname 渲染页面
10
+ */
11
+ export function PageRender({pathname}: PageRenderProps) {
12
+ const appData = useAppData()
13
+ const matchArr = matchRoutes(appData.clientRoutes, pathname)
14
+
15
+ if (matchArr != null) {
16
+ const matchResult = matchArr[matchArr.length - 1].route
17
+ if (matchResult && matchResult.element) {
18
+ return matchResult.element
19
+ }
20
+ }
21
+
22
+ return (
23
+ <Result
24
+ status={404}
25
+ title='页面不存在!'
26
+ subTitle={<div>路由地址:{pathname}</div>}
27
+ />
28
+ )
29
+ }
@@ -27,6 +27,7 @@ export class HeaderRight extends React.Component {
27
27
  await MessageUtils.alert('退出登录成功');
28
28
  history.replace('/login')
29
29
  }).catch(async e => {
30
+ console.error('[HeaderRight] 退出登录失败:', e);
30
31
  const confirm = await MessageUtils.confirm('退出登录失败,是否清空缓存');
31
32
  if (confirm) {
32
33
  localStorage.clear();