@jiangood/open-admin 3.1.6 → 3.1.8
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 +2 -5
- package/src/framework/biz/PermActions.tsx +1 -1
- package/src/framework/biz/UserPicker.less +17 -0
- package/src/framework/biz/UserPicker.tsx +296 -0
- package/src/framework/biz/index.tsx +2 -0
- package/src/framework/biz/pickerUser.ts +19 -0
- package/src/framework/components/ContextMenu/index.tsx +11 -8
- package/src/framework/components/DownloadModal/index.tsx +7 -7
- package/src/framework/components/ErrorBoundary/index.tsx +1 -1
- package/src/framework/components/FormModal/index.tsx +4 -4
- package/src/framework/components/NamedIcon/index.tsx +1 -1
- package/src/framework/components/OrgSwitcher/index.tsx +1 -1
- package/src/framework/components/Page/index.tsx +18 -4
- package/src/framework/components/PageLoading/index.tsx +2 -2
- package/src/framework/components/ProTable/index.less +70 -8
- package/src/framework/components/ProTable/index.tsx +63 -16
- package/src/framework/components/ThemeSettings/index.tsx +173 -0
- package/src/framework/components/index.ts +1 -0
- package/src/framework/config/index.ts +44 -2
- package/src/framework/config/layoutConfig.ts +52 -0
- package/src/framework/config/themeConfig.ts +449 -14
- package/src/framework/fields/BaseRemoteSelect/index.tsx +4 -4
- package/src/framework/fields/FieldDateRange/index.tsx +1 -1
- package/src/framework/fields/FieldDictSelect/index.tsx +1 -1
- package/src/framework/fields/FieldEditor/index.tsx +2 -2
- package/src/framework/fields/FieldNumberRange/index.tsx +1 -1
- package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
- package/src/framework/fields/FieldUploadImage/index.tsx +19 -19
- package/src/framework/fields/FieldUserPicker/index.tsx +124 -0
- package/src/framework/fields/index.ts +1 -0
- package/src/framework/utils/HttpClient.ts +15 -4
- package/src/framework/utils/MessageApi.ts +16 -3
- package/src/framework/utils/UrlUtils.ts +1 -1
- package/src/framework/views/ViewText/index.tsx +1 -1
- package/src/layouts/admin/AdminTabs.less +11 -11
- package/src/layouts/admin/HeaderRight.jsx +23 -12
- package/src/layouts/admin/SinglePageLayout.jsx +36 -0
- package/src/layouts/admin/TabLayout.jsx +21 -1
- package/src/layouts/admin/index.jsx +46 -26
- package/src/layouts/admin/index.less +68 -24
- package/src/layouts/index.tsx +29 -4
- package/src/pages/article/$code.jsx +2 -2
- package/src/pages/article/article.less +13 -13
- package/src/pages/index.jsx +1 -1
- package/src/pages/public/login.jsx +29 -15
- package/src/pages/public/login.less +243 -22
- package/src/pages/standalone/forceUpdatePwd.jsx +8 -7
- package/src/pages/system/article/index.jsx +13 -15
- package/src/pages/system/codegen/index.jsx +54 -51
- package/src/pages/system/dict/index.jsx +2 -2
- package/src/pages/system/file/index.jsx +3 -3
- package/src/pages/system/job/index.jsx +57 -20
- package/src/pages/system/log/index.jsx +2 -2
- package/src/pages/system/org/index.jsx +5 -5
- package/src/pages/system/role/index.jsx +28 -55
- package/src/pages/system/role/rolePerm.jsx +1 -1
- package/src/pages/system/user/index.jsx +7 -11
- package/src/pages/system/user/userPerm.jsx +5 -5
- package/src/pages/userCenter/permView.jsx +2 -2
- package/src/style/global.less +6 -6
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {Layout, Menu, Skeleton, Watermark} from 'antd';
|
|
2
|
+
import {ConfigProvider, Layout, Menu, Skeleton, Watermark} from 'antd';
|
|
3
3
|
|
|
4
4
|
import "./index.less"
|
|
5
|
-
import {HttpClient, 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, getSiderThemeConfig, getLayoutMode, EventBus} from "../../framework";
|
|
6
6
|
|
|
7
7
|
import { HeaderRight } from "./HeaderRight";
|
|
8
8
|
import { TabLayout } from "./TabLayout";
|
|
9
|
+
import { SinglePageLayout } from "./SinglePageLayout";
|
|
9
10
|
const {Header, Sider, Content} = Layout;
|
|
10
11
|
/**
|
|
11
12
|
* 带菜单的布局,主要处理布局宇框架结构
|
|
@@ -30,9 +31,13 @@ export default class AdminLayout extends React.Component {
|
|
|
30
31
|
siteInfo: {},
|
|
31
32
|
|
|
32
33
|
headerLeftArticles: [],
|
|
34
|
+
|
|
35
|
+
layoutMode: getLayoutMode(),
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
|
|
39
|
+
unsubscribeLayout = null;
|
|
40
|
+
|
|
36
41
|
componentDidMount() {
|
|
37
42
|
// 判断是否手机端,自动收起菜单
|
|
38
43
|
|
|
@@ -45,6 +50,16 @@ export default class AdminLayout extends React.Component {
|
|
|
45
50
|
|
|
46
51
|
const siteArticles = GlobalData.getSiteArticles();
|
|
47
52
|
this.setState({headerLeftArticles: siteArticles[ARTICLE_HEADER_LEFT] || []})
|
|
53
|
+
|
|
54
|
+
this.unsubscribeLayout = EventBus.on('layoutChange', () => {
|
|
55
|
+
this.setState({layoutMode: getLayoutMode()});
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
componentWillUnmount() {
|
|
60
|
+
if (this.unsubscribeLayout) {
|
|
61
|
+
this.unsubscribeLayout();
|
|
62
|
+
}
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
|
|
@@ -60,7 +75,7 @@ export default class AdminLayout extends React.Component {
|
|
|
60
75
|
if (item.type === 'divider') {
|
|
61
76
|
return
|
|
62
77
|
}
|
|
63
|
-
item.icon = <NamedIcon name={item.icon || 'AppstoreOutlined'} style={{fontSize:
|
|
78
|
+
item.icon = <NamedIcon name={item.icon || 'AppstoreOutlined'} style={{fontSize: 16}}/>
|
|
64
79
|
})
|
|
65
80
|
|
|
66
81
|
const {topMenus} = this.classifyMenus(menuTree);
|
|
@@ -97,7 +112,7 @@ export default class AdminLayout extends React.Component {
|
|
|
97
112
|
this.setState({menuLoading: false})
|
|
98
113
|
})
|
|
99
114
|
}
|
|
100
|
-
actionRef = React.createRef()
|
|
115
|
+
actionRef = React.createRef()
|
|
101
116
|
|
|
102
117
|
classifyMenus = (menuTree) => {
|
|
103
118
|
const topMenus = [];
|
|
@@ -119,7 +134,7 @@ export default class AdminLayout extends React.Component {
|
|
|
119
134
|
const defaultGroup = {
|
|
120
135
|
key: '_default_group',
|
|
121
136
|
label: '业务模块',
|
|
122
|
-
icon: <NamedIcon name="AppstoreOutlined" style={{fontSize:
|
|
137
|
+
icon: <NamedIcon name="AppstoreOutlined" style={{fontSize: 16}}/>,
|
|
123
138
|
children: leafRootNodes,
|
|
124
139
|
type: 'directory',
|
|
125
140
|
};
|
|
@@ -189,23 +204,26 @@ export default class AdminLayout extends React.Component {
|
|
|
189
204
|
const {siteInfo, loginInfo} = this.state
|
|
190
205
|
|
|
191
206
|
return <Layout className='main-layout'>
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
{/* 侧栏单独套暗色算法,使分割线/边框在深色底上可见,并按预设覆盖侧栏底色 */}
|
|
208
|
+
<ConfigProvider theme={getSiderThemeConfig()}>
|
|
209
|
+
<Sider id='left-sider'
|
|
210
|
+
width={180}
|
|
211
|
+
collapsible
|
|
212
|
+
breakpoint={'md'}
|
|
213
|
+
>
|
|
214
|
+
<div className='sider-header'>
|
|
215
|
+
<button type="button" className='logo-img'
|
|
216
|
+
onClick={() => history.push('/')}>
|
|
217
|
+
<img src="./logo.png" alt={siteInfo.title || '首页'}/>
|
|
218
|
+
</button>
|
|
219
|
+
<h3 className='hide-on-mobile'>
|
|
220
|
+
<Link to="/" style={{color: 'var(--oa-sider-text, rgba(255,255,255,0.85))'}}>{siteInfo.title}</Link>
|
|
221
|
+
</h3>
|
|
222
|
+
</div>
|
|
223
|
+
{this.renderLeftMenu()}
|
|
224
|
+
|
|
225
|
+
</Sider>
|
|
226
|
+
</ConfigProvider>
|
|
209
227
|
|
|
210
228
|
<Layout style={{flex: 1, overflow: 'hidden'}}>
|
|
211
229
|
<Header className='header'>
|
|
@@ -250,16 +268,18 @@ return <Layout className='main-layout'>
|
|
|
250
268
|
}
|
|
251
269
|
|
|
252
270
|
renderCenterContent = () => {
|
|
253
|
-
const {siteInfo, loginInfo} = this.state
|
|
271
|
+
const {siteInfo, loginInfo, layoutMode} = this.state
|
|
254
272
|
// 即使菜单为空也直接显示页面内容,不要一直显示 Skeleton
|
|
255
|
-
const
|
|
273
|
+
const contentNode = layoutMode === 'single'
|
|
274
|
+
? <SinglePageLayout/>
|
|
275
|
+
: <TabLayout pathMenuMap={this.state.pathMenuMap}/>;
|
|
256
276
|
if (siteInfo.waterMark === true) {
|
|
257
277
|
return <Watermark content={[loginInfo.name, loginInfo.account]}>
|
|
258
|
-
{
|
|
278
|
+
{contentNode}
|
|
259
279
|
</Watermark>
|
|
260
280
|
}
|
|
261
281
|
|
|
262
|
-
return
|
|
282
|
+
return contentNode
|
|
263
283
|
};
|
|
264
284
|
}
|
|
265
285
|
|
|
@@ -6,16 +6,45 @@
|
|
|
6
6
|
height: 48px;
|
|
7
7
|
display: flex;
|
|
8
8
|
align-items: center;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
min-width: 0;
|
|
10
|
+
background-color: var(--oa-color-bg-container, #fff);
|
|
11
|
+
border-bottom: 1px solid var(--oa-color-border, #e8e8e8);
|
|
12
|
+
|
|
13
|
+
/* 顶栏文字按钮:一级菜单左侧入口与右侧入口统一交互态 */
|
|
14
|
+
.item {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
height: 100%;
|
|
18
|
+
padding: 0 0.5rem;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
color: var(--oa-color-text, rgba(0, 0, 0, 0.88));
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
background: none;
|
|
24
|
+
border: none;
|
|
25
|
+
font: inherit;
|
|
26
|
+
|
|
27
|
+
&:hover {
|
|
28
|
+
background: var(--oa-color-fill, rgba(0, 0, 0, 0.06));
|
|
29
|
+
color: var(--primary-color, #1677ff);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
11
32
|
|
|
12
33
|
.top-nav {
|
|
13
34
|
flex: 1;
|
|
35
|
+
min-width: 0;
|
|
14
36
|
display: flex;
|
|
15
37
|
align-items: center;
|
|
16
38
|
margin: 0;
|
|
17
39
|
height: 100%;
|
|
18
|
-
|
|
40
|
+
/* 一级菜单过多时横向滚动,避免被裁切 */
|
|
41
|
+
overflow-x: auto;
|
|
42
|
+
overflow-y: hidden;
|
|
43
|
+
scrollbar-width: none;
|
|
44
|
+
|
|
45
|
+
&::-webkit-scrollbar {
|
|
46
|
+
height: 0;
|
|
47
|
+
}
|
|
19
48
|
|
|
20
49
|
.top-nav-item {
|
|
21
50
|
display: inline-flex;
|
|
@@ -24,7 +53,7 @@
|
|
|
24
53
|
padding: 0 16px;
|
|
25
54
|
height: 100%;
|
|
26
55
|
cursor: pointer;
|
|
27
|
-
color: rgba(0, 0, 0, 0.65);
|
|
56
|
+
color: var(--oa-color-text-secondary, rgba(0, 0, 0, 0.65));
|
|
28
57
|
transition: color 0.2s;
|
|
29
58
|
white-space: nowrap;
|
|
30
59
|
background: none;
|
|
@@ -32,36 +61,27 @@
|
|
|
32
61
|
font: inherit;
|
|
33
62
|
|
|
34
63
|
&:hover {
|
|
35
|
-
color: var(--primary-color,
|
|
64
|
+
color: var(--primary-color, #1677ff);
|
|
65
|
+
background: var(--oa-color-fill, rgba(0, 0, 0, 0.06));
|
|
36
66
|
}
|
|
37
67
|
|
|
38
68
|
&.active {
|
|
39
|
-
color: var(--primary-color,
|
|
40
|
-
box-shadow: inset 0 -2px 0 var(--primary-color,
|
|
69
|
+
color: var(--primary-color, #1677ff);
|
|
70
|
+
box-shadow: inset 0 -2px 0 var(--primary-color, #1677ff);
|
|
41
71
|
}
|
|
42
72
|
}
|
|
43
73
|
}
|
|
44
74
|
|
|
75
|
+
/* 组织切换等固定宽度控件不被压缩 */
|
|
76
|
+
.ant-select {
|
|
77
|
+
flex-shrink: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
45
80
|
.header-right {
|
|
46
81
|
line-height: 36px;
|
|
47
82
|
display: flex;
|
|
48
83
|
align-items: center;
|
|
49
84
|
flex-shrink: 0;
|
|
50
|
-
|
|
51
|
-
.item {
|
|
52
|
-
display: inline-flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
padding: 0 0.5rem;
|
|
55
|
-
cursor: pointer;
|
|
56
|
-
background: none;
|
|
57
|
-
border: none;
|
|
58
|
-
font: inherit;
|
|
59
|
-
|
|
60
|
-
&:hover {
|
|
61
|
-
background: var(--primary-color-hover, #4990CD);
|
|
62
|
-
color: white; /* NOSONAR: 主题色悬停态,对比度由主题色决定 */
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
85
|
}
|
|
66
86
|
|
|
67
87
|
}
|
|
@@ -75,7 +95,11 @@
|
|
|
75
95
|
display: flex;
|
|
76
96
|
align-items: center;
|
|
77
97
|
gap: 8px;
|
|
78
|
-
|
|
98
|
+
/* 与顶栏同高,保证 logo 基线与一级菜单对齐 */
|
|
99
|
+
height: 48px;
|
|
100
|
+
padding: 0 16px;
|
|
101
|
+
/* 底边线:与右侧顶栏/标签栏分隔线连成一线;色值由侧栏文字变量派生,避免硬编码 */
|
|
102
|
+
border-bottom: 1px solid color-mix(in srgb, var(--oa-sider-text, rgba(255, 255, 255, 0.85)) 14%, transparent);
|
|
79
103
|
|
|
80
104
|
.logo-img {
|
|
81
105
|
height: 36px;
|
|
@@ -95,10 +119,25 @@
|
|
|
95
119
|
margin: 0;
|
|
96
120
|
font-size: 14px;
|
|
97
121
|
line-height: 1;
|
|
122
|
+
/* 允许在 flex 中收缩,长站点名省略号截断而不溢出侧栏 */
|
|
123
|
+
min-width: 0;
|
|
124
|
+
overflow: hidden;
|
|
98
125
|
white-space: nowrap;
|
|
126
|
+
|
|
127
|
+
a {
|
|
128
|
+
display: block;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
text-overflow: ellipsis;
|
|
131
|
+
}
|
|
99
132
|
}
|
|
100
133
|
}
|
|
101
134
|
|
|
135
|
+
/* 折叠态:logo 居中,去掉左侧内边距 */
|
|
136
|
+
&.ant-layout-sider-collapsed .sider-header {
|
|
137
|
+
justify-content: center;
|
|
138
|
+
padding-inline: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
102
141
|
&.ant-layout-sider-collapsed .sider-header h3 {
|
|
103
142
|
display: none;
|
|
104
143
|
}
|
|
@@ -109,9 +148,14 @@
|
|
|
109
148
|
scrollbar-width: thin;
|
|
110
149
|
scrollbar-color: unset;
|
|
111
150
|
|
|
151
|
+
/* 菜单分隔线:加大上下间距,让不同菜单分组更清晰 */
|
|
152
|
+
.ant-menu-item-divider {
|
|
153
|
+
margin-block: 8px;
|
|
154
|
+
}
|
|
155
|
+
|
|
112
156
|
.ant-menu-submenu-selected {
|
|
113
157
|
&>div {
|
|
114
|
-
color:
|
|
158
|
+
color: var(--oa-sider-text, rgba(255, 255, 255, 0.85));
|
|
115
159
|
}
|
|
116
160
|
}
|
|
117
161
|
|
package/src/layouts/index.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {ConfigProvider, Modal} from "antd";
|
|
2
|
+
import {App, 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 {ErrorBoundary, GlobalData, HttpClient, PageFrame, PageLoading, PageUtils, history, getThemeConfig, setThemeColors, EventBus} from "../framework";
|
|
6
|
+
import {ErrorBoundary, GlobalData, HttpClient, PageFrame, PageLoading, PageUtils, history, getThemeConfig, getThemeMode, setThemeColors, setMessageApi, EventBus} from "../framework";
|
|
7
7
|
import type {ThemeColors} from "../framework";
|
|
8
8
|
|
|
9
9
|
import AdminLayout from "./admin"
|
|
@@ -25,6 +25,16 @@ export interface HeaderExtraContext {
|
|
|
25
25
|
loginInfo: { id: string; name: string; account: string };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/** 将 App 上下文的 message 实例注入框架,使提示跟随 ConfigProvider 主题(含暗色) */
|
|
29
|
+
function MessageBridge() {
|
|
30
|
+
const {message} = App.useApp();
|
|
31
|
+
React.useEffect(() => {
|
|
32
|
+
setMessageApi(message);
|
|
33
|
+
return () => setMessageApi();
|
|
34
|
+
}, [message]);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
interface LayoutsProps {
|
|
29
39
|
headerExtra?: (context: HeaderExtraContext) => React.ReactNode;
|
|
30
40
|
showOrgSwitcher?: (context: HeaderExtraContext) => boolean;
|
|
@@ -38,11 +48,14 @@ export class Layouts extends React.Component<LayoutsProps> {
|
|
|
38
48
|
siteInfoLoaded: false,
|
|
39
49
|
loginChecked: false,
|
|
40
50
|
loginExpiredVisible: false,
|
|
51
|
+
themeMode: getThemeMode(),
|
|
52
|
+
themeVersion: 0,
|
|
41
53
|
};
|
|
42
54
|
|
|
43
55
|
unlisten: (() => void) | null = null;
|
|
44
56
|
unsubscribeLoginExpired: (() => void) | null = null;
|
|
45
57
|
unsubscribeLogoutSuccess: (() => void) | null = null;
|
|
58
|
+
unsubscribeTheme: (() => void) | null = null;
|
|
46
59
|
|
|
47
60
|
constructor(props: LayoutsProps) {
|
|
48
61
|
super(props);
|
|
@@ -111,6 +124,12 @@ export class Layouts extends React.Component<LayoutsProps> {
|
|
|
111
124
|
this.unsubscribeLogoutSuccess = EventBus.on('logoutSuccess', () => {
|
|
112
125
|
this.setState({loginChecked: false});
|
|
113
126
|
});
|
|
127
|
+
this.unsubscribeTheme = EventBus.on('themeChange', () => {
|
|
128
|
+
this.setState(prevState => ({
|
|
129
|
+
themeMode: getThemeMode(),
|
|
130
|
+
themeVersion: prevState.themeVersion + 1,
|
|
131
|
+
}));
|
|
132
|
+
});
|
|
114
133
|
this.loadData();
|
|
115
134
|
}
|
|
116
135
|
|
|
@@ -130,6 +149,9 @@ export class Layouts extends React.Component<LayoutsProps> {
|
|
|
130
149
|
if (this.unsubscribeLogoutSuccess) {
|
|
131
150
|
this.unsubscribeLogoutSuccess();
|
|
132
151
|
}
|
|
152
|
+
if (this.unsubscribeTheme) {
|
|
153
|
+
this.unsubscribeTheme();
|
|
154
|
+
}
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
render() {
|
|
@@ -142,8 +164,11 @@ export class Layouts extends React.Component<LayoutsProps> {
|
|
|
142
164
|
return (
|
|
143
165
|
<ErrorBoundary minimal>
|
|
144
166
|
<ConfigProvider {...baseConfigProps} theme={getThemeConfig()}>
|
|
145
|
-
{
|
|
146
|
-
|
|
167
|
+
<App component={false}>
|
|
168
|
+
<MessageBridge/>
|
|
169
|
+
{this.renderContent(showPageFrame, ready, pathname, search)}
|
|
170
|
+
{this.renderLoginExpiredModal()}
|
|
171
|
+
</App>
|
|
147
172
|
</ConfigProvider>
|
|
148
173
|
</ErrorBoundary>
|
|
149
174
|
);
|
|
@@ -26,13 +26,13 @@ export default class ArticlePage extends React.Component {
|
|
|
26
26
|
const {article, loading} = this.state
|
|
27
27
|
|
|
28
28
|
if (loading) {
|
|
29
|
-
return <Page
|
|
29
|
+
return <Page>
|
|
30
30
|
<div>加载中...</div>
|
|
31
31
|
</Page>
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if (!article) {
|
|
35
|
-
return <Page
|
|
35
|
+
return <Page>
|
|
36
36
|
<div>文章不存在或已禁用</div>
|
|
37
37
|
</Page>
|
|
38
38
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
.oa-article {
|
|
2
2
|
max-width: 800px;
|
|
3
3
|
margin: 0 auto;
|
|
4
|
-
background: #fff;
|
|
4
|
+
background: var(--oa-color-bg-container, #fff);
|
|
5
5
|
padding: 24px 32px;
|
|
6
6
|
border-radius: 4px;
|
|
7
7
|
font-size: 15px;
|
|
8
8
|
line-height: 1.8;
|
|
9
|
-
color: rgba(0, 0, 0, 0.88);
|
|
9
|
+
color: var(--oa-color-text, rgba(0, 0, 0, 0.88));
|
|
10
10
|
word-wrap: break-word;
|
|
11
11
|
|
|
12
12
|
.oa-article-title {
|
|
13
13
|
text-align: center;
|
|
14
14
|
padding-bottom: 16px;
|
|
15
15
|
margin-bottom: 16px;
|
|
16
|
-
border-bottom: 1px solid #eee;
|
|
16
|
+
border-bottom: 1px solid var(--oa-color-border, #eee);
|
|
17
17
|
|
|
18
18
|
h1 {
|
|
19
19
|
font-size: 26px;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.oa-article-meta {
|
|
26
|
-
color: #999;
|
|
26
|
+
color: var(--oa-color-text-tertiary, #999);
|
|
27
27
|
font-size: 13px;
|
|
28
28
|
display: flex;
|
|
29
29
|
justify-content: center;
|
|
@@ -64,19 +64,19 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
a {
|
|
67
|
-
color: #1677ff;
|
|
67
|
+
color: var(--primary-color, #1677ff);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
blockquote {
|
|
71
71
|
margin: 0 0 16px;
|
|
72
72
|
padding: 8px 16px;
|
|
73
|
-
border-left: 4px solid #e8e8e8;
|
|
74
|
-
background: #fafafa;
|
|
75
|
-
color: #666;
|
|
73
|
+
border-left: 4px solid var(--oa-color-border, #e8e8e8);
|
|
74
|
+
background: var(--oa-color-fill, #fafafa);
|
|
75
|
+
color: var(--oa-color-text-secondary, #666);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
pre {
|
|
79
|
-
background: #f6f8fa;
|
|
79
|
+
background: var(--oa-color-fill, #f6f8fa);
|
|
80
80
|
padding: 12px 16px;
|
|
81
81
|
border-radius: 4px;
|
|
82
82
|
overflow-x: auto;
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
code {
|
|
87
|
-
background: #f6f8fa;
|
|
87
|
+
background: var(--oa-color-fill, #f6f8fa);
|
|
88
88
|
padding: 2px 4px;
|
|
89
89
|
border-radius: 3px;
|
|
90
90
|
font-size: 90%;
|
|
@@ -102,19 +102,19 @@
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
th, td {
|
|
105
|
-
border: 1px solid #e0e0e0;
|
|
105
|
+
border: 1px solid var(--oa-color-border, #e0e0e0);
|
|
106
106
|
padding: 8px 12px;
|
|
107
107
|
text-align: left;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
th {
|
|
111
|
-
background: #f5f5f5;
|
|
111
|
+
background: var(--oa-color-fill, #f5f5f5);
|
|
112
112
|
font-weight: 600;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
hr {
|
|
116
116
|
border: none;
|
|
117
|
-
border-top: 1px solid #eee;
|
|
117
|
+
border-top: 1px solid var(--oa-color-border, #eee);
|
|
118
118
|
margin: 24px 0;
|
|
119
119
|
}
|
|
120
120
|
|
package/src/pages/index.jsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {Button, Form, Input
|
|
3
|
-
import {LockOutlined, UserOutlined, WarningOutlined} from '@ant-design/icons';
|
|
4
|
-
import {EventBus, HttpClient, GlobalData, history} from "../../framework";
|
|
2
|
+
import {Button, Form, Input} from 'antd';
|
|
3
|
+
import {LockOutlined, SafetyCertificateOutlined, UserOutlined, WarningOutlined} from '@ant-design/icons';
|
|
4
|
+
import {EventBus, HttpClient, GlobalData, getMessageApi, history} from "../../framework";
|
|
5
5
|
|
|
6
6
|
import "./login.less"
|
|
7
7
|
|
|
@@ -20,7 +20,7 @@ function postLogin(values, query, success, error) {
|
|
|
20
20
|
success?.(rs)
|
|
21
21
|
}).catch(e => {
|
|
22
22
|
console.error('[Login] 登录失败:', e);
|
|
23
|
-
|
|
23
|
+
getMessageApi().error(HttpClient.errToMsg(e))
|
|
24
24
|
error?.(e)
|
|
25
25
|
})
|
|
26
26
|
}
|
|
@@ -36,7 +36,9 @@ export default class LoginPage extends React.Component {
|
|
|
36
36
|
|
|
37
37
|
state = {
|
|
38
38
|
logging: false,
|
|
39
|
-
siteInfo: {}
|
|
39
|
+
siteInfo: {},
|
|
40
|
+
/* logo.png 缺失或加载失败时,回退到矢量标记,避免首屏出现破损图 */
|
|
41
|
+
logoError: false
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
componentDidMount() {
|
|
@@ -51,7 +53,7 @@ export default class LoginPage extends React.Component {
|
|
|
51
53
|
this.setState({siteInfo: rs.data})
|
|
52
54
|
}).catch(() => {
|
|
53
55
|
console.error('[Login] 加载站点信息失败');
|
|
54
|
-
|
|
56
|
+
getMessageApi().error('加载站点信息失败,请刷新页面重试')
|
|
55
57
|
})
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -62,7 +64,7 @@ export default class LoginPage extends React.Component {
|
|
|
62
64
|
} catch (e) {
|
|
63
65
|
console.error('[Login] 密码编码失败:', e);
|
|
64
66
|
this.setState({logging: false})
|
|
65
|
-
|
|
67
|
+
getMessageApi().error('密码含不支持字符,请联系管理员重置')
|
|
66
68
|
return
|
|
67
69
|
}
|
|
68
70
|
postLogin(values, this.props.location?.query, () => {
|
|
@@ -73,8 +75,6 @@ export default class LoginPage extends React.Component {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
render() {
|
|
76
|
-
const {siteInfo} = this.state
|
|
77
|
-
|
|
78
78
|
const pageStyle = {
|
|
79
79
|
backgroundImage: `url("./login_bg.jpg")`
|
|
80
80
|
}
|
|
@@ -82,7 +82,7 @@ export default class LoginPage extends React.Component {
|
|
|
82
82
|
return (
|
|
83
83
|
<section className='login-page' style={pageStyle}>
|
|
84
84
|
<div className="login-content">
|
|
85
|
-
|
|
85
|
+
{this.renderBrand()}
|
|
86
86
|
{this.getForm()}
|
|
87
87
|
{this.renderFormBottom()}
|
|
88
88
|
</div>
|
|
@@ -90,6 +90,20 @@ export default class LoginPage extends React.Component {
|
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
renderBrand() {
|
|
94
|
+
const {siteInfo = {}, logoError} = this.state
|
|
95
|
+
return (
|
|
96
|
+
<div className="login-brand">
|
|
97
|
+
<span className="login-brand-logo">
|
|
98
|
+
{logoError
|
|
99
|
+
? <SafetyCertificateOutlined/>
|
|
100
|
+
: <img src="./logo.png" alt="" onError={() => this.setState({logoError: true})}/>}
|
|
101
|
+
</span>
|
|
102
|
+
{siteInfo.title && <h1>{siteInfo.title}</h1>}
|
|
103
|
+
</div>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
getForm = () => {
|
|
94
108
|
const form = (
|
|
95
109
|
<Form
|
|
@@ -101,15 +115,15 @@ export default class LoginPage extends React.Component {
|
|
|
101
115
|
colon={false}
|
|
102
116
|
>
|
|
103
117
|
<Form.Item name="username" rules={[{required: true, message: '请输入用户名!'}]}>
|
|
104
|
-
<Input size='large' prefix={<UserOutlined/>} placeholder="用户名" autoComplete="
|
|
118
|
+
<Input size='large' prefix={<UserOutlined/>} placeholder="用户名" autoComplete="username"/>
|
|
105
119
|
</Form.Item>
|
|
106
120
|
<Form.Item name="password" rules={[{required: true, message: '请输入密码!'}]}>
|
|
107
|
-
<Input autoComplete="
|
|
121
|
+
<Input.Password autoComplete="current-password" prefix={<LockOutlined/>} placeholder="密码"
|
|
108
122
|
size='large'
|
|
109
123
|
/>
|
|
110
124
|
</Form.Item>
|
|
111
125
|
|
|
112
|
-
<Form.Item
|
|
126
|
+
<Form.Item className="login-submit">
|
|
113
127
|
<Button loading={this.state.logging} type="primary" htmlType="submit"
|
|
114
128
|
block size='large'>
|
|
115
129
|
登录
|
|
@@ -125,10 +139,10 @@ export default class LoginPage extends React.Component {
|
|
|
125
139
|
};
|
|
126
140
|
|
|
127
141
|
renderFormBottom() {
|
|
128
|
-
const siteInfo = this.state.siteInfo;
|
|
142
|
+
const siteInfo = this.state.siteInfo || {};
|
|
129
143
|
if (siteInfo.loginBoxBottomTip) {
|
|
130
144
|
return (
|
|
131
|
-
<div
|
|
145
|
+
<div className="login-bottom-tip">
|
|
132
146
|
<WarningOutlined/> {siteInfo.loginBoxBottomTip}
|
|
133
147
|
</div>
|
|
134
148
|
)
|