@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.
Files changed (41) hide show
  1. package/package.json +2 -5
  2. package/src/framework/biz/UserPicker.less +17 -0
  3. package/src/framework/biz/UserPicker.tsx +296 -0
  4. package/src/framework/biz/index.tsx +2 -0
  5. package/src/framework/biz/pickerUser.ts +19 -0
  6. package/src/framework/components/ContextMenu/index.tsx +11 -8
  7. package/src/framework/components/DownloadModal/index.tsx +4 -4
  8. package/src/framework/components/ErrorBoundary/index.tsx +1 -1
  9. package/src/framework/components/OrgSwitcher/index.tsx +1 -1
  10. package/src/framework/components/ProTable/index.less +26 -8
  11. package/src/framework/components/ProTable/index.tsx +9 -3
  12. package/src/framework/components/ThemeSettings/index.tsx +173 -0
  13. package/src/framework/components/index.ts +1 -0
  14. package/src/framework/config/index.ts +44 -2
  15. package/src/framework/config/layoutConfig.ts +52 -0
  16. package/src/framework/config/themeConfig.ts +449 -14
  17. package/src/framework/fields/FieldEditor/index.tsx +2 -2
  18. package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
  19. package/src/framework/fields/FieldUploadImage/index.tsx +15 -15
  20. package/src/framework/fields/FieldUserPicker/index.tsx +124 -0
  21. package/src/framework/fields/index.ts +1 -0
  22. package/src/framework/utils/HttpClient.ts +15 -4
  23. package/src/framework/utils/MessageApi.ts +16 -3
  24. package/src/framework/views/ViewText/index.tsx +1 -1
  25. package/src/layouts/admin/AdminTabs.less +11 -11
  26. package/src/layouts/admin/HeaderRight.jsx +23 -12
  27. package/src/layouts/admin/SinglePageLayout.jsx +36 -0
  28. package/src/layouts/admin/TabLayout.jsx +21 -1
  29. package/src/layouts/admin/index.jsx +45 -25
  30. package/src/layouts/admin/index.less +43 -23
  31. package/src/layouts/index.tsx +29 -4
  32. package/src/pages/article/article.less +13 -13
  33. package/src/pages/public/login.jsx +29 -15
  34. package/src/pages/public/login.less +243 -22
  35. package/src/pages/standalone/forceUpdatePwd.jsx +8 -7
  36. package/src/pages/system/codegen/index.jsx +56 -51
  37. package/src/pages/system/job/index.jsx +44 -7
  38. package/src/pages/system/role/index.jsx +17 -43
  39. package/src/pages/system/user/index.jsx +0 -4
  40. package/src/pages/system/user/userPerm.jsx +3 -3
  41. package/src/style/global.less +6 -6
@@ -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
- {this.renderContent(showPageFrame, ready, pathname, search)}
146
- {this.renderLoginExpiredModal()}
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, message} from 'antd';
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
- message.error(HttpClient.errToMsg(e))
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
- message.error('加载站点信息失败,请刷新页面重试')
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
- message.error('密码含不支持字符,请联系管理员重置')
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
- <h1>{siteInfo.title}</h1>
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="off"/>
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="off" prefix={<LockOutlined/>} type="password" placeholder="密码"
121
+ <Input.Password autoComplete="current-password" prefix={<LockOutlined/>} placeholder="密码"
108
122
  size='large'
109
123
  />
110
124
  </Form.Item>
111
125
 
112
- <Form.Item style={{marginTop: 10}}>
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 style={{color: 'white', marginTop: 50, fontSize: '14px', textAlign: 'center'}}>
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
- padding: 40px;
12
- background: #15184b44;
13
- width: 350px;
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
- top: 20%;
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
- .ant-form-item-label > label{
19
- color: white;
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
- @media screen and (max-width: 768px) {
25
- .login-page {
26
- padding-top: 30%;
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
- .login-content {
29
- width: calc(100% - 100px);
30
- padding: 24px;
31
- position: static;
32
- margin: 0 auto;
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
- max-width: 300px;
40
- margin: 0 auto;
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
- .login-page.login-page h1 {
47
- margin-bottom: 50px;
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
- color: white;
50
- font-size: x-large;
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, message} from "antd";
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
- message.success('修改密码成功,请重新登录');
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: '#f0f2f5'
27
+ background: String(token.colorBgLayout)
27
28
  }}>
28
29
  <div style={{
29
- background: '#fff',
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: '#999', marginBottom: 24}}>
38
+ <p style={{textAlign: 'center', color: String(token.colorTextTertiary), marginBottom: 24}}>
38
39
  首次登录或密码已被重置,请修改密码后重新登录
39
40
  </p>
40
41
  <Form onFinish={this.onFinish}>