@jiangood/open-admin 3.1.6 → 3.1.7
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/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 +4 -4
- package/src/framework/components/ErrorBoundary/index.tsx +1 -1
- package/src/framework/components/OrgSwitcher/index.tsx +1 -1
- package/src/framework/components/ProTable/index.less +26 -8
- package/src/framework/components/ProTable/index.tsx +9 -3
- 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/FieldEditor/index.tsx +2 -2
- package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
- package/src/framework/fields/FieldUploadImage/index.tsx +15 -15
- 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/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 +45 -25
- package/src/layouts/admin/index.less +43 -23
- package/src/layouts/index.tsx +29 -4
- package/src/pages/article/article.less +13 -13
- 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/codegen/index.jsx +56 -51
- package/src/pages/system/job/index.jsx +44 -7
- package/src/pages/system/role/index.jsx +17 -43
- package/src/pages/system/user/index.jsx +0 -4
- package/src/pages/system/user/userPerm.jsx +3 -3
- package/src/style/global.less +6 -6
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
|
);
|
|
@@ -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
|
|
|
@@ -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
|
)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
.login-page {
|
|
2
|
+
/* 背景图加载前的兜底底色,保证白色文字始终可读 */
|
|
3
|
+
background-color: #16233f;
|
|
2
4
|
background-size: cover;
|
|
3
5
|
background-repeat: no-repeat;
|
|
4
6
|
background-position: bottom;
|
|
@@ -6,47 +8,266 @@
|
|
|
6
8
|
width: 100vw;
|
|
7
9
|
height: 100vh;
|
|
8
10
|
|
|
9
|
-
|
|
10
11
|
.login-content {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/* 面板内复用的局部变量,统一玻璃质感与文字层级 */
|
|
13
|
+
--login-text: rgba(255, 255, 255, 0.94);
|
|
14
|
+
--login-text-dim: rgba(255, 255, 255, 0.6);
|
|
15
|
+
--login-hairline: rgba(255, 255, 255, 0.16);
|
|
16
|
+
--login-field-bg: rgba(255, 255, 255, 0.08);
|
|
17
|
+
--login-radius: calc(var(--oa-border-radius, 6px) + 10px);
|
|
18
|
+
--login-field-radius: calc(var(--oa-border-radius, 6px) + 2px);
|
|
19
|
+
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
width: 384px;
|
|
22
|
+
max-width: calc(100vw - 48px);
|
|
23
|
+
max-height: calc(100vh - 48px);
|
|
24
|
+
overflow: auto;
|
|
14
25
|
position: fixed;
|
|
15
26
|
right: 15%;
|
|
16
|
-
|
|
27
|
+
/* 垂直居中,矮屏下不再溢出 */
|
|
28
|
+
top: 50%;
|
|
29
|
+
transform: translateY(-50%);
|
|
30
|
+
padding: 40px 36px 30px;
|
|
31
|
+
|
|
32
|
+
/* 深色磨砂玻璃:主题深色底(保证层次)+ 顶部主色微光 + backdrop-filter 虚化 */
|
|
33
|
+
background-color: color-mix(in srgb, var(--oa-sider-bg, #0b1220) 55%, transparent);
|
|
34
|
+
background-image: linear-gradient(
|
|
35
|
+
180deg,
|
|
36
|
+
color-mix(in srgb, var(--primary-color, #1677ff) 16%, transparent) 0%,
|
|
37
|
+
transparent 140px
|
|
38
|
+
);
|
|
39
|
+
backdrop-filter: blur(20px) saturate(140%);
|
|
40
|
+
-webkit-backdrop-filter: blur(20px) saturate(140%);
|
|
41
|
+
/* 细描边沿用主题主色;顶边内高光模拟玻璃反光 */
|
|
42
|
+
border: 1px solid color-mix(in srgb, var(--primary-color, #1677ff) 26%, var(--login-hairline));
|
|
43
|
+
border-radius: var(--login-radius);
|
|
44
|
+
box-shadow: 0 24px 64px -20px rgba(0, 0, 0, 0.6),
|
|
45
|
+
0 4px 16px rgba(0, 0, 0, 0.25),
|
|
46
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.14);
|
|
17
47
|
|
|
18
|
-
|
|
19
|
-
|
|
48
|
+
animation: login-panel-in 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
49
|
+
|
|
50
|
+
.ant-form-item-label > label {
|
|
51
|
+
color: var(--login-text);
|
|
52
|
+
/* 玻璃更透明后,浅色文字加轻微阴影保证可读性 */
|
|
53
|
+
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
|
20
54
|
}
|
|
21
55
|
}
|
|
22
56
|
}
|
|
23
57
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
58
|
+
/* 面板入场:轻微上浮淡入 */
|
|
59
|
+
@keyframes login-panel-in {
|
|
60
|
+
from {
|
|
61
|
+
opacity: 0;
|
|
62
|
+
transform: translateY(calc(-50% + 16px));
|
|
63
|
+
}
|
|
64
|
+
to {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
transform: translateY(-50%);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
27
69
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
70
|
+
/* 不支持 backdrop-filter 的浏览器:提高底色不透明度,保证白字对比度 */
|
|
71
|
+
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
72
|
+
.login-page .login-content {
|
|
73
|
+
background-color: color-mix(in srgb, var(--oa-sider-bg, #0b1220) 92%, transparent);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* 品牌区:logo + 站点标题,与后台侧栏保持同一品牌识别 */
|
|
78
|
+
.login-brand {
|
|
79
|
+
margin-bottom: 26px;
|
|
80
|
+
text-align: center;
|
|
81
|
+
|
|
82
|
+
.login-brand-logo {
|
|
83
|
+
display: grid;
|
|
84
|
+
place-items: center;
|
|
85
|
+
width: 56px;
|
|
86
|
+
height: 56px;
|
|
87
|
+
margin: 0 auto 14px;
|
|
88
|
+
border-radius: var(--login-radius);
|
|
89
|
+
background: color-mix(in srgb, var(--primary-color, #1677ff) 16%, rgba(255, 255, 255, 0.06));
|
|
90
|
+
border: 1px solid var(--login-hairline);
|
|
91
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
|
92
|
+
|
|
93
|
+
img {
|
|
94
|
+
width: 38px;
|
|
95
|
+
height: 38px;
|
|
96
|
+
object-fit: contain;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* logo 加载失败时回退到此矢量标记 */
|
|
100
|
+
.anticon {
|
|
101
|
+
color: var(--login-text);
|
|
102
|
+
font-size: 26px;
|
|
33
103
|
}
|
|
34
104
|
}
|
|
35
105
|
}
|
|
36
106
|
|
|
107
|
+
.login-page.login-page h1 {
|
|
108
|
+
margin: 0;
|
|
109
|
+
text-align: center;
|
|
110
|
+
color: var(--login-text);
|
|
111
|
+
font-size: 22px;
|
|
112
|
+
font-weight: 600;
|
|
113
|
+
letter-spacing: 0.5px;
|
|
114
|
+
line-height: 1.4;
|
|
115
|
+
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
|
|
116
|
+
}
|
|
37
117
|
|
|
118
|
+
/* 表单:占满面板,输入框改为玻璃质感并跟随主题主色聚焦 */
|
|
38
119
|
.login-form {
|
|
39
|
-
|
|
40
|
-
margin: 0
|
|
41
|
-
}
|
|
120
|
+
width: 100%;
|
|
121
|
+
margin: 0;
|
|
42
122
|
|
|
123
|
+
.ant-form-item {
|
|
124
|
+
margin-bottom: 18px;
|
|
125
|
+
}
|
|
43
126
|
|
|
127
|
+
.ant-input-affix-wrapper {
|
|
128
|
+
height: 46px;
|
|
129
|
+
padding: 0 14px;
|
|
130
|
+
border-radius: var(--login-field-radius);
|
|
131
|
+
background-color: var(--login-field-bg);
|
|
132
|
+
border: 1px solid var(--login-hairline);
|
|
133
|
+
box-shadow: none;
|
|
134
|
+
transition: background-color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
|
44
135
|
|
|
136
|
+
&:hover {
|
|
137
|
+
background-color: rgba(255, 255, 255, 0.12);
|
|
138
|
+
border-color: rgba(255, 255, 255, 0.28);
|
|
139
|
+
}
|
|
45
140
|
|
|
46
|
-
|
|
47
|
-
|
|
141
|
+
&:focus-within,
|
|
142
|
+
&.ant-input-affix-wrapper-focused {
|
|
143
|
+
background-color: rgba(255, 255, 255, 0.14);
|
|
144
|
+
border-color: var(--primary-color, #1677ff);
|
|
145
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary-color, #1677ff) 28%, transparent);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.ant-input-prefix {
|
|
149
|
+
margin-inline-end: 10px;
|
|
150
|
+
color: rgba(255, 255, 255, 0.55);
|
|
151
|
+
font-size: 16px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* 密码可见性切换按钮 */
|
|
155
|
+
.ant-input-suffix {
|
|
156
|
+
color: rgba(255, 255, 255, 0.55);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.ant-input {
|
|
161
|
+
color: var(--login-text);
|
|
162
|
+
font-size: 15px;
|
|
163
|
+
background: transparent;
|
|
164
|
+
|
|
165
|
+
&::placeholder {
|
|
166
|
+
color: var(--login-text-dim);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* 浏览器自动填充时保持玻璃底与浅色文字 */
|
|
170
|
+
&:-webkit-autofill {
|
|
171
|
+
-webkit-text-fill-color: var(--login-text);
|
|
172
|
+
caret-color: var(--login-text);
|
|
173
|
+
transition: background-color 9999s ease-out;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ant-form-item-explain-error {
|
|
178
|
+
margin-top: 4px;
|
|
179
|
+
color: color-mix(in srgb, var(--oa-color-error, #ff4d4f) 55%, #fff);
|
|
180
|
+
font-size: 13px;
|
|
181
|
+
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* 提交按钮:主色渐变 + 悬浮辉光,作为面板唯一强视觉锚点 */
|
|
185
|
+
.login-submit {
|
|
186
|
+
margin-bottom: 0;
|
|
187
|
+
|
|
188
|
+
.ant-btn-primary {
|
|
189
|
+
height: 46px;
|
|
190
|
+
border: none;
|
|
191
|
+
border-radius: var(--login-field-radius);
|
|
192
|
+
font-size: 16px;
|
|
193
|
+
font-weight: 600;
|
|
194
|
+
letter-spacing: 0.3em;
|
|
195
|
+
text-indent: 0.3em;
|
|
196
|
+
background: linear-gradient(
|
|
197
|
+
135deg,
|
|
198
|
+
var(--primary-color, #1677ff),
|
|
199
|
+
var(--primary-color-hover, #4096ff)
|
|
200
|
+
);
|
|
201
|
+
box-shadow: 0 12px 26px -12px color-mix(in srgb, var(--primary-color, #1677ff) 85%, transparent);
|
|
202
|
+
transition: filter 0.2s, box-shadow 0.2s, transform 0.16s;
|
|
203
|
+
|
|
204
|
+
&:hover:not(:disabled) {
|
|
205
|
+
filter: brightness(1.07);
|
|
206
|
+
box-shadow: 0 16px 32px -12px color-mix(in srgb, var(--primary-color, #1677ff) 95%, transparent);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
&:active:not(:disabled) {
|
|
210
|
+
transform: translateY(1px);
|
|
211
|
+
filter: brightness(0.98);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* 校验失败态:描边与文案提亮到深色玻璃上仍可读。
|
|
218
|
+
多加 .login-page 提升特异性,盖过 antd 聚焦态注入的白底 */
|
|
219
|
+
.login-page .login-form .ant-form-item-has-error {
|
|
220
|
+
.ant-input-affix-wrapper,
|
|
221
|
+
.ant-input-affix-wrapper:hover,
|
|
222
|
+
.ant-input-affix-wrapper:focus,
|
|
223
|
+
.ant-input-affix-wrapper:focus-within {
|
|
224
|
+
background-color: color-mix(in srgb, var(--oa-color-error, #ff4d4f) 12%, transparent);
|
|
225
|
+
border-color: color-mix(in srgb, var(--oa-color-error, #ff4d4f) 70%, transparent);
|
|
226
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--oa-color-error, #ff4d4f) 24%, transparent);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.ant-input {
|
|
230
|
+
background-color: transparent;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* 登录框底部提示:与表单之间用分隔线区隔,保持次要文字层级 */
|
|
235
|
+
.login-bottom-tip {
|
|
236
|
+
display: flex;
|
|
237
|
+
align-items: center;
|
|
238
|
+
justify-content: center;
|
|
239
|
+
gap: 8px;
|
|
240
|
+
margin-top: 22px;
|
|
241
|
+
padding-top: 18px;
|
|
242
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
243
|
+
color: var(--login-text-dim);
|
|
244
|
+
font-size: 13px;
|
|
245
|
+
line-height: 1.5;
|
|
48
246
|
text-align: center;
|
|
49
|
-
|
|
50
|
-
|
|
247
|
+
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
|
248
|
+
|
|
249
|
+
.anticon {
|
|
250
|
+
flex: none;
|
|
251
|
+
}
|
|
51
252
|
}
|
|
52
253
|
|
|
254
|
+
@media (prefers-reduced-motion: reduce) {
|
|
255
|
+
.login-page .login-content {
|
|
256
|
+
animation: none;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@media screen and (max-width: 768px) {
|
|
261
|
+
.login-page {
|
|
262
|
+
padding-top: 20%;
|
|
263
|
+
|
|
264
|
+
.login-content {
|
|
265
|
+
width: calc(100% - 64px);
|
|
266
|
+
padding: 28px 22px;
|
|
267
|
+
position: static;
|
|
268
|
+
margin: 0 auto;
|
|
269
|
+
transform: none;
|
|
270
|
+
animation: none;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {Button, Form, Input
|
|
3
|
-
import {HttpClient, history} from "../../framework";
|
|
2
|
+
import {Button, Form, Input} from "antd";
|
|
3
|
+
import {getMessageApi, getToken, HttpClient, history} from "../../framework";
|
|
4
4
|
|
|
5
5
|
export default class ForceUpdatePwdPage extends React.Component {
|
|
6
6
|
|
|
7
7
|
onFinish = (values) => {
|
|
8
8
|
HttpClient.post('admin/userCenter/update-pwd', values, null).then(() => {
|
|
9
|
-
|
|
9
|
+
getMessageApi().success('修改密码成功,请重新登录');
|
|
10
10
|
history.push('/public/login');
|
|
11
11
|
})
|
|
12
12
|
}
|
|
@@ -17,24 +17,25 @@ export default class ForceUpdatePwdPage extends React.Component {
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
render() {
|
|
20
|
+
const token = getToken();
|
|
20
21
|
return (
|
|
21
22
|
<div style={{
|
|
22
23
|
display: 'flex',
|
|
23
24
|
justifyContent: 'center',
|
|
24
25
|
alignItems: 'center',
|
|
25
26
|
minHeight: '100vh',
|
|
26
|
-
background:
|
|
27
|
+
background: String(token.colorBgLayout)
|
|
27
28
|
}}>
|
|
28
29
|
<div style={{
|
|
29
|
-
background:
|
|
30
|
+
background: String(token.colorBgContainer),
|
|
30
31
|
padding: '40px',
|
|
31
32
|
borderRadius: '8px',
|
|
32
|
-
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
|
33
|
+
boxShadow: String(token.boxShadowSecondary ?? '0 2px 8px rgba(0,0,0,0.1)'),
|
|
33
34
|
maxWidth: 400,
|
|
34
35
|
width: '100%'
|
|
35
36
|
}}>
|
|
36
37
|
<h2 style={{textAlign: 'center', marginBottom: 24}}>修改密码</h2>
|
|
37
|
-
<p style={{textAlign: 'center', color:
|
|
38
|
+
<p style={{textAlign: 'center', color: String(token.colorTextTertiary), marginBottom: 24}}>
|
|
38
39
|
首次登录或密码已被重置,请修改密码后重新登录
|
|
39
40
|
</p>
|
|
40
41
|
<Form onFinish={this.onFinish}>
|