@jiangood/open-admin 3.0.8 → 3.0.10

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 CHANGED
@@ -48,5 +48,5 @@
48
48
  "test:e2e": "playwright test",
49
49
  "test:e2e:ui": "playwright test --ui"
50
50
  },
51
- "version": "3.0.8"
51
+ "version": "3.0.10"
52
52
  }
@@ -1 +1,2 @@
1
- export { getThemeConfig, getToken } from './themeConfig'
1
+ export { getThemeConfig, getToken, setThemeColors } from './themeConfig'
2
+ export type { ThemeColors } from './themeConfig'
@@ -3,37 +3,60 @@ import type { ThemeConfig } from 'antd';
3
3
 
4
4
  const { getDesignToken } = theme;
5
5
 
6
+ export interface ThemeColors {
7
+ colorPrimary?: string;
8
+ colorSuccess?: string;
9
+ colorWarning?: string;
10
+ colorError?: string;
11
+ colorBgLayout?: string;
12
+ }
13
+
14
+ export const DEFAULT_COLORS: Required<ThemeColors> = {
15
+ colorPrimary: '#1961AC',
16
+ colorSuccess: '#52c41a',
17
+ colorWarning: '#faad14',
18
+ colorError: '#ff4d4f',
19
+ colorBgLayout: '#f5f5f5',
20
+ };
21
+
22
+ let overrideColors: Partial<ThemeColors> | null = null;
6
23
  let cachedConfig: ThemeConfig | null = null;
7
24
  let cachedToken: Record<string, any> | null = null;
8
25
 
9
26
  function buildConfig(): ThemeConfig {
10
- const token: Record<string, any> = { borderRadius: 4 };
11
- const components: Record<string, any> = {
12
- Layout: { headerBg: 'white', triggerHeight: 32 },
13
- };
14
-
15
- try {
16
- const env = import.meta.env;
17
- if (env.VITE_THEME_PRIMARY_COLOR) token.colorPrimary = env.VITE_THEME_PRIMARY_COLOR;
18
- if (env.VITE_THEME_SUCCESS_COLOR) token.colorSuccess = env.VITE_THEME_SUCCESS_COLOR;
19
- if (env.VITE_THEME_WARNING_COLOR) token.colorWarning = env.VITE_THEME_WARNING_COLOR;
20
- if (env.VITE_THEME_ERROR_COLOR) token.colorError = env.VITE_THEME_ERROR_COLOR;
21
- if (env.VITE_THEME_BACKGROUND_COLOR) token.colorBgLayout = env.VITE_THEME_BACKGROUND_COLOR;
22
- } catch (e) {}
23
-
24
- return { token, components };
27
+ const token: Record<string, any> = { borderRadius: 4, ...DEFAULT_COLORS, ...overrideColors };
28
+ const components: Record<string, any> = {
29
+ Layout: { headerBg: 'white', triggerHeight: 32 },
30
+ };
31
+
32
+ return { token, components };
33
+ }
34
+
35
+ function syncCssVars(token: Record<string, any>) {
36
+ if (typeof document === 'undefined') return;
37
+ const el = document.documentElement;
38
+ el.style.setProperty('--primary-color', token.colorPrimary);
39
+ el.style.setProperty('--primary-color-hover', token.colorPrimaryHover || token.colorPrimary);
40
+ }
41
+
42
+ /** 由 Layouts 在渲染前调用,传入业务侧颜色覆盖;不传则恢复默认主题 */
43
+ export function setThemeColors(colors?: Partial<ThemeColors>) {
44
+ overrideColors = colors || null;
45
+ cachedConfig = null;
46
+ cachedToken = null;
47
+ syncCssVars(getToken());
25
48
  }
26
49
 
27
50
  export function getThemeConfig(): ThemeConfig {
28
- if (!cachedConfig) {
29
- cachedConfig = buildConfig();
30
- }
31
- return cachedConfig;
51
+ if (!cachedConfig) {
52
+ cachedConfig = buildConfig();
53
+ }
54
+ return cachedConfig;
32
55
  }
33
56
 
34
57
  export function getToken(): Record<string, any> {
35
- if (!cachedToken) {
36
- cachedToken = getDesignToken(getThemeConfig());
37
- }
38
- return cachedToken;
58
+ if (!cachedToken) {
59
+ cachedToken = getDesignToken(getThemeConfig());
60
+ }
61
+ return cachedToken;
39
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, ARTICLE_HEADER_AVATAR_DROPDOWN, ARTICLE_HEADER_RIGHT} from "../../framework";
3
+ import {DeviceUtils, HttpUtils, 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
 
@@ -100,6 +100,7 @@ export class HeaderRight extends React.Component {
100
100
  onCancel={() => this.setState({alertVisible: false})}
101
101
  onOk={() => {
102
102
  this.setState({alertVisible: false});
103
+ EventBus.emit('logoutSuccess');
103
104
  history.replace('/public/login')
104
105
  }}>
105
106
  退出登录成功
@@ -109,6 +110,7 @@ export class HeaderRight extends React.Component {
109
110
  onOk={() => {
110
111
  this.setState({confirmVisible: false});
111
112
  localStorage.clear();
113
+ EventBus.emit('logoutSuccess');
112
114
  history.replace('/public/login')
113
115
  }}>
114
116
  退出登录失败,是否清空缓存
@@ -6,20 +6,20 @@ import 'dayjs/locale/zh-cn';
6
6
  import {history, PageFrame} from "../framework";
7
7
 
8
8
  import AdminLayout from "./admin"
9
- import {HttpUtils, PageLoading, PageUtils, GlobalData, getThemeConfig, EventBus} from "../framework";
9
+ import {HttpUtils, PageLoading, PageUtils, GlobalData, getThemeConfig, setThemeColors, EventBus} from "../framework";
10
10
  import {ErrorBoundary} from "../framework";
11
+ import type {ThemeColors} from "../framework";
11
12
 
12
13
  import '../style/global.less'
13
14
  import './index.less'
14
15
 
15
16
  dayjs.locale('zh-cn');
16
17
 
17
- const configProps = {
18
+ const baseConfigProps = {
18
19
  input: {autoComplete: 'off'},
19
20
  form: {validateMessages: {required: '必填项'}, colon: false},
20
21
  button: {autoInsertSpace: false},
21
22
  locale: zhCN,
22
- theme: getThemeConfig(),
23
23
  };
24
24
 
25
25
  export interface HeaderExtraContext {
@@ -30,6 +30,8 @@ export interface HeaderExtraContext {
30
30
  interface LayoutsProps {
31
31
  headerExtra?: (context: HeaderExtraContext) => React.ReactNode;
32
32
  showOrgSwitcher?: (context: HeaderExtraContext) => boolean;
33
+ /** 主题颜色覆盖,不传使用框架默认主题 */
34
+ colors?: Partial<ThemeColors>;
33
35
  }
34
36
 
35
37
  export class Layouts extends React.Component<LayoutsProps> {
@@ -42,6 +44,12 @@ export class Layouts extends React.Component<LayoutsProps> {
42
44
 
43
45
  unlisten: (() => void) | null = null;
44
46
  unsubscribeLoginExpired: (() => void) | null = null;
47
+ unsubscribeLogoutSuccess: (() => void) | null = null;
48
+
49
+ constructor(props: LayoutsProps) {
50
+ super(props);
51
+ setThemeColors(props.colors);
52
+ }
45
53
 
46
54
  onLocationChange = ({location}: { location: typeof history.location }) => {
47
55
  this.setState({location});
@@ -101,6 +109,9 @@ export class Layouts extends React.Component<LayoutsProps> {
101
109
  this.setState({loginExpiredVisible: true});
102
110
  }
103
111
  });
112
+ this.unsubscribeLogoutSuccess = EventBus.on('logoutSuccess', () => {
113
+ this.setState({loginChecked: false});
114
+ });
104
115
  this.loadData();
105
116
  }
106
117
 
@@ -117,6 +128,9 @@ export class Layouts extends React.Component<LayoutsProps> {
117
128
  if (this.unsubscribeLoginExpired) {
118
129
  this.unsubscribeLoginExpired();
119
130
  }
131
+ if (this.unsubscribeLogoutSuccess) {
132
+ this.unsubscribeLogoutSuccess();
133
+ }
120
134
  }
121
135
 
122
136
  render() {
@@ -128,7 +142,7 @@ export class Layouts extends React.Component<LayoutsProps> {
128
142
 
129
143
  return (
130
144
  <ErrorBoundary minimal>
131
- <ConfigProvider {...configProps}>
145
+ <ConfigProvider {...baseConfigProps} theme={getThemeConfig()}>
132
146
  {showPageFrame ? <PageFrame url={pathname + search}/> : ready ? <AdminLayout headerExtra={this.props.headerExtra} showOrgSwitcher={this.props.showOrgSwitcher} loginInfo={GlobalData.getLoginInfo()}/> : (
133
147
  <PageLoading messages={[
134
148
  !siteInfoLoaded && '加载站点信息...',
@@ -146,7 +160,7 @@ export class Layouts extends React.Component<LayoutsProps> {
146
160
  <Modal open={this.state.loginExpiredVisible} title="确认操作" okText="确定"
147
161
  onCancel={() => this.setState({loginExpiredVisible: false})}
148
162
  onOk={() => {
149
- this.setState({loginExpiredVisible: false});
163
+ this.setState({loginExpiredVisible: false, loginChecked: false});
150
164
  PageUtils.redirectToLogin();
151
165
  }}>
152
166
  登录已过期,请重新登录
@@ -43,7 +43,7 @@ export default class extends React.Component {
43
43
 
44
44
  <Card title="个人信息"
45
45
  style={{marginBottom: 16}}
46
- extra={<Button onClick={() => this.setState({changePwdOpen: true})}>修改密码</Button>}>
46
+ extra={<Button type="primary" danger onClick={() => this.setState({changePwdOpen: true})}>修改密码</Button>}>
47
47
  <Descriptions column={2} size="middle">
48
48
  <Descriptions.Item label="用户名称">{info.name}</Descriptions.Item>
49
49
  <Descriptions.Item label="账号">{info.account}</Descriptions.Item>