@jiangood/open-admin 3.0.14 → 3.1.0

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 (80) hide show
  1. package/package.json +15 -2
  2. package/src/framework/biz/OrgTree.tsx +10 -6
  3. package/src/framework/biz/PermActions.tsx +5 -3
  4. package/src/framework/biz/RoleTree.tsx +10 -6
  5. package/src/framework/biz/index.tsx +1 -0
  6. package/src/framework/components/ContextMenu/index.tsx +17 -1
  7. package/src/framework/components/DownloadModal/index.tsx +40 -33
  8. package/src/framework/components/FormModal/index.tsx +10 -8
  9. package/src/framework/components/NamedIcon/index.tsx +2 -2
  10. package/src/framework/components/OrgSwitcher/index.tsx +6 -7
  11. package/src/framework/components/PageLoading/index.tsx +3 -3
  12. package/src/framework/components/ProTable/index.tsx +28 -25
  13. package/src/framework/components/ValueType/index.tsx +1 -1
  14. package/src/framework/config/themeConfig.ts +5 -5
  15. package/src/framework/fields/BaseRemoteSelect/index.tsx +23 -21
  16. package/src/framework/fields/FieldBoolean/index.tsx +6 -6
  17. package/src/framework/fields/FieldDate/index.tsx +10 -10
  18. package/src/framework/fields/FieldDateRange/index.tsx +20 -20
  19. package/src/framework/fields/FieldDictSelect/index.tsx +2 -2
  20. package/src/framework/fields/FieldEditor/index.tsx +1 -1
  21. package/src/framework/fields/FieldNumberRange/index.tsx +16 -17
  22. package/src/framework/fields/FieldPercent/index.tsx +2 -2
  23. package/src/framework/fields/FieldRemoteSelect/index.tsx +4 -4
  24. package/src/framework/fields/FieldRemoteTree/index.tsx +1 -1
  25. package/src/framework/fields/FieldRemoteTreeCascader/index.tsx +1 -1
  26. package/src/framework/fields/FieldRemoteTreeSelect/index.tsx +1 -1
  27. package/src/framework/fields/FieldSysOrgTree/index.tsx +1 -1
  28. package/src/framework/fields/FieldSysOrgTreeSelect/index.tsx +1 -1
  29. package/src/framework/fields/FieldTable/index.tsx +15 -13
  30. package/src/framework/fields/FieldTableSelect/index.tsx +5 -5
  31. package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
  32. package/src/framework/fields/FieldUploadImage/index.tsx +31 -29
  33. package/src/framework/fields/types.ts +0 -2
  34. package/src/framework/router/PageFrame.tsx +1 -1
  35. package/src/framework/router/matcher.ts +2 -2
  36. package/src/framework/utils/ArrayUtils.ts +2 -2
  37. package/src/framework/utils/DateUtils.ts +1 -1
  38. package/src/framework/utils/{system/DictUtils.ts → DictUtils.ts} +1 -1
  39. package/src/framework/utils/EventBus.ts +7 -7
  40. package/src/framework/utils/HttpClient.ts +283 -0
  41. package/src/framework/utils/{system/PageUtils.ts → PageUtils.ts} +4 -4
  42. package/src/framework/utils/{system/PermUtils.ts → PermUtils.ts} +3 -3
  43. package/src/framework/utils/StringUtils.ts +10 -8
  44. package/src/framework/utils/TreeUtils.ts +3 -3
  45. package/src/framework/utils/UrlUtils.ts +3 -2
  46. package/src/framework/utils/index.ts +4 -1
  47. package/src/framework/views/ViewApproveStatus/index.tsx +1 -2
  48. package/src/framework/views/ViewBoolean/index.tsx +4 -1
  49. package/src/framework/views/ViewFile/index.tsx +4 -4
  50. package/src/framework/views/ViewImage/index.tsx +2 -2
  51. package/src/framework/views/ViewPassword/index.tsx +3 -2
  52. package/src/framework/views/ViewText/index.tsx +12 -3
  53. package/src/framework/views/types.ts +2 -4
  54. package/src/layouts/admin/AdminTabs.jsx +9 -0
  55. package/src/layouts/admin/AdminTabs.less +2 -2
  56. package/src/layouts/admin/HeaderRight.jsx +8 -7
  57. package/src/layouts/admin/TabLayout.jsx +2 -2
  58. package/src/layouts/admin/index.jsx +15 -11
  59. package/src/layouts/admin/index.less +16 -1
  60. package/src/layouts/index.tsx +24 -18
  61. package/src/pages/404.jsx +1 -1
  62. package/src/pages/article/$code.jsx +4 -4
  63. package/src/pages/index.jsx +1 -1
  64. package/src/pages/public/login.jsx +34 -27
  65. package/src/pages/public/test.jsx +1 -1
  66. package/src/pages/standalone/forceUpdatePwd.jsx +9 -7
  67. package/src/pages/system/article/index.jsx +9 -8
  68. package/src/pages/system/dict/index.jsx +19 -16
  69. package/src/pages/system/file/index.jsx +9 -9
  70. package/src/pages/system/job/index.jsx +17 -17
  71. package/src/pages/system/log/index.jsx +5 -7
  72. package/src/pages/system/org/index.jsx +31 -18
  73. package/src/pages/system/role/index.jsx +13 -12
  74. package/src/pages/system/role/rolePerm.jsx +25 -21
  75. package/src/pages/system/user/index.jsx +35 -33
  76. package/src/pages/system/user/userPerm.jsx +12 -12
  77. package/src/pages/userCenter/index.jsx +11 -9
  78. package/src/pages/userCenter/permView.jsx +7 -7
  79. package/src/framework/utils/system/HttpUtils.ts +0 -97
  80. package/src/framework/utils/system/index.ts +0 -4
@@ -3,14 +3,14 @@ export class StringUtils {
3
3
  static readonly ISO_SPLITTER: string = "/";
4
4
 
5
5
  static removePrefix(str: string | null | undefined, ch: string): string {
6
- if (str != null && str.startsWith(ch)) {
6
+ if (str?.startsWith(ch)) {
7
7
  return str.substring(ch.length);
8
8
  }
9
9
  return str ?? '';
10
10
  }
11
11
 
12
12
  static removeSuffix(str: string | null | undefined, ch: string): string {
13
- if (str != null && str.endsWith(ch)) {
13
+ if (str?.endsWith(ch)) {
14
14
  return str.substring(0, str.length - ch.length);
15
15
  }
16
16
  return str ?? '';
@@ -18,9 +18,11 @@ export class StringUtils {
18
18
 
19
19
  static random(length: number): string {
20
20
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
21
+ const values = new Uint32Array(length);
22
+ crypto.getRandomValues(values);
21
23
  let result = '';
22
24
  for (let i = 0; i < length; i++) {
23
- result += characters.charAt(Math.floor(Math.random() * characters.length));
25
+ result += characters.charAt(values[i] % characters.length);
24
26
  }
25
27
  return result;
26
28
  }
@@ -61,7 +63,7 @@ export class StringUtils {
61
63
  static getWidth(str: string | null | undefined): number {
62
64
  if (str == null || str.length === 0) return 0;
63
65
  return str.split('').reduce((pre, cur) => {
64
- const charCode = cur.charCodeAt(0);
66
+ const charCode = cur.codePointAt(0);
65
67
  return pre + (charCode >= 0 && charCode <= 128 ? 1 : 2);
66
68
  }, 0);
67
69
  }
@@ -69,7 +71,7 @@ export class StringUtils {
69
71
  static cutByWidth(str: string, maxWidth: number): string {
70
72
  let showLength = 0;
71
73
  return str.split('').reduce((pre, cur) => {
72
- const charCode = cur.charCodeAt(0);
74
+ const charCode = cur.codePointAt(0);
73
75
  const charWidth = (charCode >= 0 && charCode <= 128) ? 1 : 2;
74
76
  if (showLength + charWidth <= maxWidth) {
75
77
  showLength += charWidth;
@@ -88,17 +90,17 @@ export class StringUtils {
88
90
  return StringUtils.cutByWidth(str, len) + suffix;
89
91
  }
90
92
 
91
- static isString(value: any): value is string {
93
+ static isString(value: unknown): value is string {
92
94
  return typeof value === 'string';
93
95
  }
94
96
 
95
- static split(str: any, sp: string): string[] {
97
+ static split(str: string | string[] | null | undefined, sp: string): string[] {
96
98
  if (str == null || str.length === 0) return [];
97
99
  if (Array.isArray(str)) return str;
98
100
  return str.split(sp);
99
101
  }
100
102
 
101
- static join(arr: any, sp: string): string {
103
+ static join(arr: unknown, sp: string): string {
102
104
  if (arr == null || !Array.isArray(arr)) {
103
105
  return '';
104
106
  }
@@ -1,8 +1,8 @@
1
- export interface TreeNode<T = any> {
1
+ export interface TreeNode<T = unknown> {
2
2
  id: string | number;
3
3
  pid: string | number | null;
4
4
  children?: TreeNode<T>[];
5
- [key: string]: any;
5
+ [key: string]: unknown;
6
6
  }
7
7
 
8
8
  export class TreeUtils {
@@ -19,7 +19,7 @@ export class TreeUtils {
19
19
  static findByKey<T extends TreeNode>(key: string | number, list: T[], keyName: keyof T | 'id' = 'id'): T | undefined {
20
20
  for (const item of list) {
21
21
  if (item[keyName as keyof T] === key) return item;
22
- if (item.children && item.children.length) {
22
+ if (item.children?.length) {
23
23
  const rs = TreeUtils.findByKey(key, item.children as T[], keyName);
24
24
  if (rs) return rs;
25
25
  }
@@ -28,7 +28,7 @@ export class UrlUtils {
28
28
  if (!params) return "";
29
29
  const buffer: string[] = [];
30
30
  for (const k in params) {
31
- if (Object.prototype.hasOwnProperty.call(params, k)) {
31
+ if (Object.hasOwn(params, k)) {
32
32
  const v = params[k];
33
33
  if (v !== null && v !== undefined) {
34
34
  buffer.push(`${k}=${v}`);
@@ -66,6 +66,7 @@ export class UrlUtils {
66
66
  */
67
67
  static contextPath(path: string): string {
68
68
  const base = import.meta.env.VITE_SERVER_SERVLET_CONTEXT_PATH;
69
- return base + path;
69
+ if (base === '/' || base === '') return path;
70
+ return base.replace(/\/+$/, '') + path; // NOSONAR: 需去除尾部斜杠,正则已最简
70
71
  }
71
72
  }
@@ -7,6 +7,9 @@ export * from "./TreeUtils";
7
7
  export * from './StorageUtils'
8
8
  export * from './DeviceUtils'
9
9
  export * from './ObjectUtils'
10
- export * from './system'
10
+ export * from './DictUtils'
11
+ export * from './PermUtils'
12
+ export * from './PageUtils'
13
+ export * from './HttpClient'
11
14
  export * from './MessageApi'
12
15
  export * from './ModalApi'
@@ -1,9 +1,8 @@
1
1
  import {DictUtils} from "../../utils";
2
- import {Tag} from "antd";
3
2
  import {Component} from "react";
4
3
  import type {ViewProps} from "../types";
5
4
 
6
- export class ViewApproveStatus extends Component<ViewProps<String>> {
5
+ export class ViewApproveStatus extends Component<ViewProps<string>> {
7
6
  render() {
8
7
  const {value} = this.props;
9
8
  return DictUtils.dictTag('approveStatus', value);
@@ -4,6 +4,9 @@ import type {ViewProps} from "../types";
4
4
  export class ViewBoolean extends React.Component<ViewProps<boolean>> {
5
5
  render() {
6
6
  const {value} = this.props;
7
- return value == null ? null : (value ? '是' : '否');
7
+ if (value == null) {
8
+ return null;
9
+ }
10
+ return value ? '是' : '否';
8
11
  }
9
12
  }
@@ -23,8 +23,8 @@ export class ViewFile extends React.Component {
23
23
  return <iframe
24
24
  src={url}
25
25
  width='99%'
26
- frameBorder={0}
27
- style={{height}}
26
+ title="文件预览"
27
+ style={{height, border: 0}}
28
28
  />
29
29
  }
30
30
 
@@ -34,8 +34,8 @@ export class ViewFile extends React.Component {
34
34
  <iframe
35
35
  src={url}
36
36
  width='99%'
37
- frameBorder={0}
38
- style={{height}}
37
+ title="文件预览"
38
+ style={{height, border: 0}}
39
39
  />
40
40
  </div>
41
41
  });
@@ -16,7 +16,7 @@ import {UrlUtils} from '../../utils';
16
16
  */
17
17
  export class ViewImage extends React.Component {
18
18
 
19
- static previewGroupProps = {
19
+ static readonly previewGroupProps = {
20
20
  preview: {
21
21
  scaleStep: 0.3,
22
22
  },
@@ -61,7 +61,7 @@ export class ViewImage extends React.Component {
61
61
 
62
62
  return (
63
63
  <Image.PreviewGroup preview={previewConfig}>
64
- {items.map((item, i) => (
64
+ {items.map((item) => (
65
65
  <Image
66
66
  key={item.url}
67
67
  src={item.thumb}
@@ -16,9 +16,10 @@ export class ViewPassword extends React.Component {
16
16
  const visible = this.state.visible;
17
17
  return <Space>
18
18
  <span>{this.state.visible ? v : '******'}</span>
19
- <a onClick={() => this.setState({visible: !visible})}>
19
+ <button type="button" style={{background: 'none', border: 'none', padding: 0, font: 'inherit', cursor: 'pointer'}}
20
+ onClick={() => this.setState({visible: !visible})}>
20
21
  {visible ? <EyeOutlined/> : <EyeInvisibleOutlined/>}
21
- </a>
22
+ </button>
22
23
  </Space>
23
24
  }
24
25
  }
@@ -8,7 +8,7 @@ export class ViewText extends React.Component {
8
8
  modalOpen: false,
9
9
  };
10
10
 
11
- static defaultProps = {
11
+ static readonly defaultProps = {
12
12
  ellipsis: false,
13
13
  maxLength: 15
14
14
  }
@@ -27,9 +27,18 @@ export class ViewText extends React.Component {
27
27
  const short = StringUtils.ellipsis(value, maxLength);
28
28
  return (
29
29
  <Tooltip placement="topLeft" title={value}>
30
- <span style={{cursor: 'pointer', borderBottom: '1px dashed #d9d9d9'}} onClick={() => this.setState({modalOpen: true})}>
30
+ <button type="button"
31
+ style={{
32
+ background: 'none',
33
+ border: 'none',
34
+ borderBottom: '1px dashed #d9d9d9',
35
+ padding: 0,
36
+ font: 'inherit',
37
+ cursor: 'pointer',
38
+ }}
39
+ onClick={() => this.setState({modalOpen: true})}>
31
40
  {short}
32
- </span>
41
+ </button>
33
42
  <Modal open={this.state.modalOpen} title="长文本内容" width={700} footer={null}
34
43
  onCancel={() => this.setState({modalOpen: false})}>
35
44
  <div style={{height: 500, overflowY: 'auto'}}>{value}</div>
@@ -15,12 +15,10 @@ export interface ViewProps<T> {
15
15
  * 通用视图组件类类型
16
16
  * @template T - value 的类型
17
17
  */
18
- export interface ViewComponent<T> extends React.ComponentClass<ViewProps<T> & any> {
19
- }
18
+ export type ViewComponent<T> = React.ComponentClass<ViewProps<T>>;
20
19
 
21
20
  /**
22
21
  * 通用视图组件函数类型
23
22
  * @template T - value 的类型
24
23
  */
25
- export interface ViewFunctionComponent<T> extends React.FC<ViewProps<T> & any> {
26
- }
24
+ export type ViewFunctionComponent<T> = React.FC<ViewProps<T>>;
@@ -43,7 +43,16 @@ export class AdminTabs extends React.Component {
43
43
  <div
44
44
  key={tab.key}
45
45
  className={`admin-tab${tab.key === activeKey ? ' active' : ''}`}
46
+ role="tab"
47
+ tabIndex={tab.key === activeKey ? 0 : -1}
48
+ aria-selected={tab.key === activeKey}
46
49
  onClick={() => onChange(tab.key)}
50
+ onKeyDown={e => {
51
+ if (e.key === 'Enter' || e.key === ' ') {
52
+ e.preventDefault();
53
+ onChange(tab.key);
54
+ }
55
+ }}
47
56
  onContextMenu={e => this.handleContextMenu(e, tab.key)}
48
57
  >
49
58
  <span className="admin-tab-label">{tab.label}</span>
@@ -52,11 +52,11 @@
52
52
  height: 16px;
53
53
  font-size: 12px;
54
54
  border-radius: 50%;
55
- color: rgba(0, 0, 0, 0.45);
55
+ color: rgba(0, 0, 0, 0.45); /* NOSONAR: 次要文本颜色,设计如此 */
56
56
 
57
57
  &:hover {
58
58
  background: rgba(0, 0, 0, 0.08);
59
- color: rgba(0, 0, 0, 0.88);
59
+ color: rgba(0, 0, 0, 0.88); /* NOSONAR: 悬停态主文本色,对比度设计如此 */
60
60
  }
61
61
  }
62
62
  }
@@ -1,6 +1,6 @@
1
1
  import {Avatar, Dropdown, Modal} from "antd";
2
2
  import React from "react";
3
- import {DeviceUtils, HttpUtils, PageUtils, GlobalData, getToken, history, EventBus, ARTICLE_HEADER_AVATAR_DROPDOWN, ARTICLE_HEADER_RIGHT} from "../../framework";
3
+ import {DeviceUtils, HttpClient, PageUtils, GlobalData, getToken, history, EventBus, ARTICLE_HEADER_AVATAR_DROPDOWN, ARTICLE_HEADER_RIGHT} from "../../framework";
4
4
 
5
5
  export class HeaderRight extends React.Component {
6
6
 
@@ -28,10 +28,10 @@ export class HeaderRight extends React.Component {
28
28
  }
29
29
 
30
30
  logout = () => {
31
- HttpUtils.post('admin/auth/logout').then(async () => {
31
+ HttpClient.post('admin/auth/logout', null, null, {toastError: false}).then(() => {
32
32
  localStorage.clear()
33
33
  this.setState({alertVisible: true})
34
- }).catch(async e => {
34
+ }).catch(e => {
35
35
  console.error('[HeaderRight] 退出登录失败:', e);
36
36
  this.setState({confirmVisible: true})
37
37
  })
@@ -51,7 +51,8 @@ export class HeaderRight extends React.Component {
51
51
 
52
52
  if (this.state.isMobileDevice) {
53
53
  return <div className='header-right'>
54
- <a onClick={this.logout}>退出</a>
54
+ <button type="button" style={{background: 'none', border: 'none', padding: 0, font: 'inherit', cursor: 'pointer'}}
55
+ onClick={this.logout}>退出</button>
55
56
  </div>
56
57
  }
57
58
 
@@ -68,10 +69,10 @@ export class HeaderRight extends React.Component {
68
69
 
69
70
  return <div className='header-right'>
70
71
  {headerArticles.map(a => (
71
- <div key={a.code} className='item' style={{cursor: 'pointer'}}
72
- onClick={() => this.openArticle(a.code, a.title)}>
72
+ <button key={a.code} type="button" className='item'
73
+ onClick={() => this.openArticle(a.code, a.title)}>
73
74
  {a.title}
74
- </div>
75
+ </button>
75
76
  ))}
76
77
 
77
78
  <Dropdown menu={{
@@ -34,7 +34,7 @@ export class TabLayout extends React.Component {
34
34
  const pathname = qIndex === -1 ? url : url.substring(0, qIndex);
35
35
  const search = qIndex === -1 ? '' : url.substring(qIndex);
36
36
  this.setState(prev => ({
37
- tabs: prev.tabs.find(t => t.key === url) ? prev.tabs
37
+ tabs: prev.tabs.some(t => t.key === url) ? prev.tabs
38
38
  : [...prev.tabs, {key: url, label: getLabel(pathname, search, this.props.pathMenuMap), refreshKey: 0}],
39
39
  activeKey: url,
40
40
  }));
@@ -42,7 +42,7 @@ export class TabLayout extends React.Component {
42
42
 
43
43
  closeTab = (key) => {
44
44
  this.setState(prev => {
45
- if (!prev.tabs.find(t => t.key === key)) return null;
45
+ if (!prev.tabs.some(t => t.key === key)) return null;
46
46
  const next = prev.tabs.filter(t => t.key !== key);
47
47
  if (next.length === 0) {
48
48
  setTimeout(() => history.push('/'), 0);
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import {Layout, Menu, Skeleton, Watermark} from 'antd';
3
3
 
4
4
  import "./index.less"
5
- import {HttpUtils, NamedIcon, PageUtils, GlobalData, TreeUtils, history, Link, ARTICLE_HEADER_LEFT, OrgSwitcher} from "../../framework";
5
+ import {HttpClient, NamedIcon, PageUtils, GlobalData, TreeUtils, history, Link, ARTICLE_HEADER_LEFT, OrgSwitcher} from "../../framework";
6
6
 
7
7
  import { HeaderRight } from "./HeaderRight";
8
8
  import { TabLayout } from "./TabLayout";
@@ -10,7 +10,7 @@ const {Header, Sider, Content} = Layout;
10
10
  /**
11
11
  * 带菜单的布局,主要处理布局宇框架结构
12
12
  */
13
- export default class extends React.Component {
13
+ export default class AdminLayout extends React.Component {
14
14
 
15
15
  state = {
16
16
  loginInfo: {},
@@ -50,8 +50,8 @@ export default class extends React.Component {
50
50
 
51
51
  initMenu = () => {
52
52
  this.setState({menuLoading: true})
53
- HttpUtils.get('/admin/menu-info').then(info => {
54
- const {menuTree, pathMenuMap, menuMap} = info
53
+ HttpClient.get('/admin/menu-info', null, {toastError: false}).then(info => {
54
+ const {menuTree, pathMenuMap, menuMap} = info.data
55
55
  this.setState({menuMap})
56
56
 
57
57
  const pathname = PageUtils.currentPathname();
@@ -86,15 +86,15 @@ export default class extends React.Component {
86
86
  }
87
87
 
88
88
  this.setState({menuTree, pathMenuMap, topMenus, sideMenus, activeTopMenuKey, currentMenuKey})
89
+ this.setState({menuLoading: false})
89
90
 
90
91
  }).catch(err => {
91
92
  console.error('加载菜单失败:', err)
92
93
  this.setState({menuTree: [], topMenus: [], sideMenus: []})
93
- }).finally(()=>{
94
94
  this.setState({menuLoading: false})
95
95
  })
96
96
  }
97
- actionRef = React.createRef()
97
+ actionRef = React.createRef() // NOSONAR: React ref 由父组件通过 ref 访问
98
98
 
99
99
  classifyMenus = (menuTree) => {
100
100
  const topMenus = [];
@@ -166,13 +166,14 @@ export default class extends React.Component {
166
166
  return (
167
167
  <nav className="top-nav">
168
168
  {topMenus.map(item => (
169
- <span
169
+ <button
170
170
  key={item.key}
171
+ type="button"
171
172
  className={'top-nav-item' + (item.key === activeTopMenuKey ? ' active' : '')}
172
173
  onClick={() => this.onTopMenuClick({key: item.key})}
173
174
  >
174
175
  {item.label}
175
- </span>
176
+ </button>
176
177
  ))}
177
178
  </nav>
178
179
  );
@@ -188,7 +189,10 @@ return <Layout className='main-layout'>
188
189
  breakpoint={'md'}
189
190
  >
190
191
  <div className='sider-header'>
191
- <img className='logo-img' src="./logo.png" onClick={() => history.push('/')} alt='logo'/>
192
+ <button type="button" className='logo-img'
193
+ onClick={() => history.push('/')}>
194
+ <img src="./logo.png" alt='logo'/>
195
+ </button>
192
196
  <h3 className='hide-on-mobile'>
193
197
  <Link to="/" style={{color: 'rgba(255,255,255,0.85)'}}>{siteInfo.title}</Link>
194
198
  </h3>
@@ -201,8 +205,8 @@ return <Layout className='main-layout'>
201
205
  <Header className='header'>
202
206
  {this.renderTopMenu()}
203
207
  {this.state.headerLeftArticles.map(a => (
204
- <div key={a.code} className='item' style={{cursor: 'pointer', padding: '0 12px', whiteSpace: 'nowrap'}}
205
- onClick={() => PageUtils.open('/article/' + a.code, a.title)}>{a.title}</div>
208
+ <button key={a.code} type="button" className='item'
209
+ onClick={() => PageUtils.open('/article/' + a.code, a.title)}>{a.title}</button>
206
210
  ))}
207
211
  {this.props.showOrgSwitcher?.({
208
212
  activeTopMenu: this.getActiveTopMenu(),
@@ -27,6 +27,9 @@
27
27
  color: rgba(0, 0, 0, 0.65);
28
28
  transition: color 0.2s;
29
29
  white-space: nowrap;
30
+ background: none;
31
+ border: none;
32
+ font: inherit;
30
33
 
31
34
  &:hover {
32
35
  color: var(--primary-color, var(--primary-color, #1961AC));
@@ -50,10 +53,13 @@
50
53
  align-items: center;
51
54
  padding: 0 0.5rem;
52
55
  cursor: pointer;
56
+ background: none;
57
+ border: none;
58
+ font: inherit;
53
59
 
54
60
  &:hover {
55
61
  background: var(--primary-color-hover, #4990CD);
56
- color: white;
62
+ color: white; /* NOSONAR: 主题色悬停态,对比度由主题色决定 */
57
63
  }
58
64
  }
59
65
  }
@@ -74,6 +80,15 @@
74
80
  .logo-img {
75
81
  height: 36px;
76
82
  cursor: pointer;
83
+ background: none;
84
+ border: none;
85
+ padding: 0;
86
+ display: inline-flex;
87
+ align-items: center;
88
+
89
+ img {
90
+ height: 36px;
91
+ }
77
92
  }
78
93
 
79
94
  h3 {
@@ -3,12 +3,10 @@ import {ConfigProvider, Modal} from "antd";
3
3
  import zhCN from 'antd/locale/zh_CN';
4
4
  import dayjs from 'dayjs';
5
5
  import 'dayjs/locale/zh-cn';
6
- import {history, PageFrame} from "../framework";
6
+ import {ErrorBoundary, GlobalData, HttpClient, PageFrame, PageLoading, PageUtils, history, getThemeConfig, setThemeColors, EventBus} from "../framework";
7
+ import type {ThemeColors} from "../framework";
7
8
 
8
9
  import AdminLayout from "./admin"
9
- import {HttpUtils, PageLoading, PageUtils, GlobalData, getThemeConfig, setThemeColors, EventBus} from "../framework";
10
- import {ErrorBoundary} from "../framework";
11
- import type {ThemeColors} from "../framework";
12
10
 
13
11
  import '../style/global.less'
14
12
  import './index.less'
@@ -76,8 +74,8 @@ export class Layouts extends React.Component<LayoutsProps> {
76
74
  }
77
75
 
78
76
  loadSiteInfo() {
79
- HttpUtils.get("/admin/public/site-info").then(data => {
80
- GlobalData.setSiteInfo(data);
77
+ HttpClient.get("/admin/public/site-info", null, {toastError: false}).then(data => {
78
+ GlobalData.setSiteInfo(data.data);
81
79
  this.setState({siteInfoLoaded: true});
82
80
  }).catch(() => {
83
81
  console.error('[Layout] 加载站点信息失败');
@@ -85,12 +83,12 @@ export class Layouts extends React.Component<LayoutsProps> {
85
83
  }
86
84
 
87
85
  loadLoginInfo() {
88
- HttpUtils.get('/admin/public/login-info').then(data => {
89
- GlobalData.setDictInfo(data.dictInfo);
90
- GlobalData.setLoginInfo(data.loginInfo);
91
- GlobalData.setSiteArticles(data.siteArticles);
86
+ HttpClient.get('/admin/public/login-info', null, {toastError: false}).then(data => {
87
+ GlobalData.setDictInfo(data.data.dictInfo);
88
+ GlobalData.setLoginInfo(data.data.loginInfo);
89
+ GlobalData.setSiteArticles(data.data.siteArticles);
92
90
 
93
- if (data.needUpdatePwd) {
91
+ if (data.data.needUpdatePwd) {
94
92
  this.setState({loginChecked: true});
95
93
  history.push('/standalone/forceUpdatePwd');
96
94
  return;
@@ -116,7 +114,7 @@ export class Layouts extends React.Component<LayoutsProps> {
116
114
  this.loadData();
117
115
  }
118
116
 
119
- componentDidUpdate(prevProps: {}, prevState: typeof this.state) {
117
+ componentDidUpdate(prevProps: LayoutsProps, prevState: typeof this.state) {
120
118
  if (this.state.location !== prevState.location) {
121
119
  this.loadData();
122
120
  }
@@ -144,18 +142,26 @@ export class Layouts extends React.Component<LayoutsProps> {
144
142
  return (
145
143
  <ErrorBoundary minimal>
146
144
  <ConfigProvider {...baseConfigProps} theme={getThemeConfig()}>
147
- {showPageFrame ? <PageFrame url={pathname + search}/> : ready ? <AdminLayout headerExtra={this.props.headerExtra} showOrgSwitcher={this.props.showOrgSwitcher} loginInfo={GlobalData.getLoginInfo()}/> : (
148
- <PageLoading messages={[
149
- !siteInfoLoaded && '加载站点信息...',
150
- !loginChecked && '检查登录中...',
151
- ].filter(Boolean)}/>
152
- )}
145
+ {this.renderContent(showPageFrame, ready, pathname, search)}
153
146
  {this.renderLoginExpiredModal()}
154
147
  </ConfigProvider>
155
148
  </ErrorBoundary>
156
149
  );
157
150
  }
158
151
 
152
+ renderContent(showPageFrame: boolean, ready: boolean, pathname: string, search: string) {
153
+ if (showPageFrame) {
154
+ return <PageFrame url={pathname + search}/>;
155
+ }
156
+ if (ready) {
157
+ return <AdminLayout headerExtra={this.props.headerExtra} showOrgSwitcher={this.props.showOrgSwitcher} loginInfo={GlobalData.getLoginInfo()}/>;
158
+ }
159
+ return <PageLoading messages={[
160
+ !this.state.siteInfoLoaded && '加载站点信息...',
161
+ !this.state.loginChecked && '检查登录中...',
162
+ ].filter(Boolean)}/>;
163
+ }
164
+
159
165
  renderLoginExpiredModal() {
160
166
  return (
161
167
  <Modal open={this.state.loginExpiredVisible} title="确认操作" okText="确定"
package/src/pages/404.jsx CHANGED
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import {Result} from "antd";
3
3
  import {Page} from "../framework";
4
4
 
5
- export default class extends React.Component {
5
+ export default class NotFound extends React.Component {
6
6
 
7
7
  render() {
8
8
  return <Page>
@@ -1,8 +1,8 @@
1
1
  import React from "react";
2
- import {HttpUtils, Page, UrlUtils} from "../../framework";
2
+ import {HttpClient, Page, UrlUtils} from "../../framework";
3
3
  import "./article.less";
4
4
 
5
- export default class extends React.Component {
5
+ export default class ArticlePage extends React.Component {
6
6
 
7
7
  state = {
8
8
  article: null,
@@ -12,8 +12,8 @@ export default class extends React.Component {
12
12
  componentDidMount() {
13
13
  const code = this.props.params?.code
14
14
  if (code) {
15
- HttpUtils.get('admin/article/getByCode', {code}).then(rs => {
16
- this.setState({article: rs, loading: false})
15
+ HttpClient.get('admin/article/getByCode', {code}, {toastError: false}).then(rs => {
16
+ this.setState({article: rs.data, loading: false})
17
17
  }).catch(() => {
18
18
  this.setState({loading: false})
19
19
  })
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import {Card} from "antd";
3
3
  import {Page} from "../framework";
4
4
 
5
- export default class extends React.Component {
5
+ export default class HomePage extends React.Component {
6
6
  render() {
7
7
  return <Page title="首页" description="欢迎使用本系统">
8
8
  <Card>欢迎使用本系统</Card>