@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
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button, Drawer, Segmented } from 'antd';
|
|
3
|
+
import { CheckOutlined } from '@ant-design/icons';
|
|
4
|
+
import {
|
|
5
|
+
THEME_PRESETS,
|
|
6
|
+
DEFAULT_COLORS,
|
|
7
|
+
DEFAULT_SIDER_BG,
|
|
8
|
+
applyThemePreset,
|
|
9
|
+
getActivePresetKey,
|
|
10
|
+
getThemeMode,
|
|
11
|
+
resetAllSettings,
|
|
12
|
+
setThemeMode,
|
|
13
|
+
DENSITY_PRESETS,
|
|
14
|
+
RADIUS_PRESETS,
|
|
15
|
+
applyDensityPreset,
|
|
16
|
+
getActiveDensityKey,
|
|
17
|
+
applyRadiusPreset,
|
|
18
|
+
getActiveRadiusKey,
|
|
19
|
+
getLayoutMode,
|
|
20
|
+
setLayoutMode,
|
|
21
|
+
} from '../../config';
|
|
22
|
+
import type { ThemeMode, ThemeDensity, ThemeRadius, LayoutMode } from '../../config';
|
|
23
|
+
import { EventBus } from '../../utils/EventBus';
|
|
24
|
+
|
|
25
|
+
export interface ThemeSettingsProps {
|
|
26
|
+
open: boolean;
|
|
27
|
+
onClose: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function SectionTitle({ children }: { children: React.ReactNode }) {
|
|
31
|
+
return <div style={{ margin: '20px 0 12px', fontWeight: 600 }}>{children}</div>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 界面设置面板:外观模式、主题色、界面密度、圆角、界面布局。
|
|
36
|
+
* 所有变更即时生效并持久化到 localStorage。
|
|
37
|
+
*/
|
|
38
|
+
export function ThemeSettings({ open, onClose }: ThemeSettingsProps) {
|
|
39
|
+
const [activeKey, setActiveKey] = React.useState(getActivePresetKey());
|
|
40
|
+
const [mode, setMode] = React.useState<ThemeMode>(getThemeMode());
|
|
41
|
+
const [density, setDensity] = React.useState<ThemeDensity>(getActiveDensityKey());
|
|
42
|
+
const [radius, setRadius] = React.useState<ThemeRadius>(getActiveRadiusKey());
|
|
43
|
+
const [layout, setLayout] = React.useState<LayoutMode>(getLayoutMode());
|
|
44
|
+
|
|
45
|
+
React.useEffect(() => {
|
|
46
|
+
const offTheme = EventBus.on('themeChange', () => {
|
|
47
|
+
setActiveKey(getActivePresetKey());
|
|
48
|
+
setMode(getThemeMode());
|
|
49
|
+
setDensity(getActiveDensityKey());
|
|
50
|
+
setRadius(getActiveRadiusKey());
|
|
51
|
+
});
|
|
52
|
+
const offLayout = EventBus.on('layoutChange', () => {
|
|
53
|
+
setLayout(getLayoutMode());
|
|
54
|
+
});
|
|
55
|
+
return () => {
|
|
56
|
+
offTheme();
|
|
57
|
+
offLayout();
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
const radiusValue = (key: ThemeRadius) =>
|
|
62
|
+
(RADIUS_PRESETS.find(p => p.key === key)?.token.borderRadius as number) ?? 6;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Drawer title="界面设置" open={open} onClose={onClose} size={340}>
|
|
66
|
+
<div style={{ fontWeight: 600 }}>外观模式</div>
|
|
67
|
+
<Segmented
|
|
68
|
+
block
|
|
69
|
+
style={{ marginTop: 12 }}
|
|
70
|
+
value={mode}
|
|
71
|
+
onChange={value => setThemeMode(value as ThemeMode)}
|
|
72
|
+
options={[
|
|
73
|
+
{ label: '浅色', value: 'light' },
|
|
74
|
+
{ label: '深色', value: 'dark' },
|
|
75
|
+
]}
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<SectionTitle>主题色</SectionTitle>
|
|
79
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12 }}>
|
|
80
|
+
{THEME_PRESETS.map(preset => {
|
|
81
|
+
const primary = preset.colors.colorPrimary || DEFAULT_COLORS.colorPrimary || '#1677ff';
|
|
82
|
+
const sider = preset.colors.siderBg || DEFAULT_SIDER_BG;
|
|
83
|
+
const active = preset.key === activeKey;
|
|
84
|
+
return (
|
|
85
|
+
<button
|
|
86
|
+
key={preset.key}
|
|
87
|
+
type="button"
|
|
88
|
+
aria-pressed={active}
|
|
89
|
+
onClick={() => applyThemePreset(preset.key)}
|
|
90
|
+
style={{
|
|
91
|
+
cursor: 'pointer',
|
|
92
|
+
padding: 0,
|
|
93
|
+
overflow: 'hidden',
|
|
94
|
+
borderRadius: 8,
|
|
95
|
+
textAlign: 'left',
|
|
96
|
+
background: 'transparent',
|
|
97
|
+
border: active
|
|
98
|
+
? `2px solid ${primary}`
|
|
99
|
+
: '1px solid var(--oa-color-border, #e8e8e8)',
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
<div style={{ display: 'flex', height: 44 }}>
|
|
103
|
+
<div
|
|
104
|
+
style={{
|
|
105
|
+
width: '50%',
|
|
106
|
+
background: primary,
|
|
107
|
+
display: 'flex',
|
|
108
|
+
alignItems: 'center',
|
|
109
|
+
justifyContent: 'center',
|
|
110
|
+
color: '#fff',
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{active && <CheckOutlined />}
|
|
114
|
+
</div>
|
|
115
|
+
<div style={{ width: '50%', background: sider }} />
|
|
116
|
+
</div>
|
|
117
|
+
<div style={{ padding: '6px 8px', fontSize: 12 }}>{preset.name}</div>
|
|
118
|
+
</button>
|
|
119
|
+
);
|
|
120
|
+
})}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<SectionTitle>界面密度</SectionTitle>
|
|
124
|
+
<Segmented
|
|
125
|
+
block
|
|
126
|
+
value={density}
|
|
127
|
+
onChange={value => applyDensityPreset(value as ThemeDensity)}
|
|
128
|
+
options={DENSITY_PRESETS.map(p => ({ label: p.name, value: p.key }))}
|
|
129
|
+
/>
|
|
130
|
+
|
|
131
|
+
<SectionTitle>圆角</SectionTitle>
|
|
132
|
+
<Segmented
|
|
133
|
+
block
|
|
134
|
+
value={radius}
|
|
135
|
+
onChange={value => applyRadiusPreset(value as ThemeRadius)}
|
|
136
|
+
options={RADIUS_PRESETS.map(p => ({
|
|
137
|
+
value: p.key,
|
|
138
|
+
label: (
|
|
139
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
|
140
|
+
<span
|
|
141
|
+
style={{
|
|
142
|
+
display: 'inline-block',
|
|
143
|
+
width: 14,
|
|
144
|
+
height: 14,
|
|
145
|
+
border: '1.5px solid currentColor',
|
|
146
|
+
borderRadius: radiusValue(p.key),
|
|
147
|
+
}}
|
|
148
|
+
/>
|
|
149
|
+
{p.name}
|
|
150
|
+
</span>
|
|
151
|
+
),
|
|
152
|
+
}))}
|
|
153
|
+
/>
|
|
154
|
+
|
|
155
|
+
<SectionTitle>界面布局</SectionTitle>
|
|
156
|
+
<Segmented
|
|
157
|
+
block
|
|
158
|
+
value={layout}
|
|
159
|
+
onChange={value => setLayoutMode(value as LayoutMode)}
|
|
160
|
+
options={[
|
|
161
|
+
{ label: '多标签页', value: 'tabs' },
|
|
162
|
+
{ label: '单页', value: 'single' },
|
|
163
|
+
]}
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
<Button block style={{ marginTop: 20 }} onClick={() => resetAllSettings()}>
|
|
167
|
+
恢复默认
|
|
168
|
+
</Button>
|
|
169
|
+
</Drawer>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export default ThemeSettings;
|
|
@@ -8,6 +8,7 @@ export * from './ValueType'
|
|
|
8
8
|
export * from './ContextMenu'
|
|
9
9
|
export * from './DownloadModal'
|
|
10
10
|
export * from './OrgSwitcher'
|
|
11
|
+
export * from './ThemeSettings'
|
|
11
12
|
export { default as ErrorBoundary } from './ErrorBoundary'
|
|
12
13
|
export { default as FormModal } from './FormModal'
|
|
13
14
|
export { default as FormDrawer } from './DrawerForm'
|
|
@@ -1,2 +1,44 @@
|
|
|
1
|
-
export {
|
|
2
|
-
|
|
1
|
+
export {
|
|
2
|
+
getThemeConfig,
|
|
3
|
+
getSiderThemeConfig,
|
|
4
|
+
getToken,
|
|
5
|
+
setThemeColors,
|
|
6
|
+
getThemeMode,
|
|
7
|
+
setThemeMode,
|
|
8
|
+
toggleThemeMode,
|
|
9
|
+
applyThemePreset,
|
|
10
|
+
getActivePresetKey,
|
|
11
|
+
applyDensityPreset,
|
|
12
|
+
getActiveDensityKey,
|
|
13
|
+
applyRadiusPreset,
|
|
14
|
+
getActiveRadiusKey,
|
|
15
|
+
resetUserTheme,
|
|
16
|
+
DEFAULT_COLORS,
|
|
17
|
+
DEFAULT_PRESET_KEY,
|
|
18
|
+
DEFAULT_SIDER_BG,
|
|
19
|
+
DEFAULT_SIDER_TRIGGER_BG,
|
|
20
|
+
THEME_PRESETS,
|
|
21
|
+
DEFAULT_DENSITY_KEY,
|
|
22
|
+
DENSITY_PRESETS,
|
|
23
|
+
DEFAULT_RADIUS_KEY,
|
|
24
|
+
RADIUS_PRESETS,
|
|
25
|
+
} from './themeConfig'
|
|
26
|
+
export type { ThemeColors, ThemeMode, ThemePreset, ThemeDensity, ThemeRadius, ThemeDensityPreset, ThemeRadiusPreset } from './themeConfig'
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
getLayoutMode,
|
|
30
|
+
setLayoutMode,
|
|
31
|
+
toggleLayoutMode,
|
|
32
|
+
resetLayoutMode,
|
|
33
|
+
DEFAULT_LAYOUT_MODE,
|
|
34
|
+
} from './layoutConfig'
|
|
35
|
+
export type { LayoutMode } from './layoutConfig'
|
|
36
|
+
|
|
37
|
+
import { resetUserTheme } from './themeConfig'
|
|
38
|
+
import { resetLayoutMode } from './layoutConfig'
|
|
39
|
+
|
|
40
|
+
/** 一键恢复全部界面设置:主题(颜色/明暗/密度/圆角)+ 布局 */
|
|
41
|
+
export function resetAllSettings() {
|
|
42
|
+
resetUserTheme()
|
|
43
|
+
resetLayoutMode()
|
|
44
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { EventBus } from '../utils/EventBus';
|
|
2
|
+
|
|
3
|
+
export type LayoutMode = 'tabs' | 'single';
|
|
4
|
+
|
|
5
|
+
const LAYOUT_MODE_KEY = 'oa-layout-tabs';
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_LAYOUT_MODE: LayoutMode = 'tabs';
|
|
8
|
+
|
|
9
|
+
const LAYOUT_MODES: LayoutMode[] = ['tabs', 'single'];
|
|
10
|
+
|
|
11
|
+
function readStoredLayoutMode(): LayoutMode {
|
|
12
|
+
if (typeof window === 'undefined') return DEFAULT_LAYOUT_MODE;
|
|
13
|
+
try {
|
|
14
|
+
const mode = localStorage.getItem(LAYOUT_MODE_KEY);
|
|
15
|
+
if (mode && LAYOUT_MODES.includes(mode as LayoutMode)) return mode as LayoutMode;
|
|
16
|
+
} catch {
|
|
17
|
+
// 隐私模式下 localStorage 不可用,忽略
|
|
18
|
+
}
|
|
19
|
+
return DEFAULT_LAYOUT_MODE;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let currentLayoutMode: LayoutMode = readStoredLayoutMode();
|
|
23
|
+
|
|
24
|
+
/** 当前布局模式:'tabs' 多标签页 / 'single' 单页 */
|
|
25
|
+
export function getLayoutMode(): LayoutMode {
|
|
26
|
+
return currentLayoutMode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 设置布局模式,持久化并广播 layoutChange 供管理布局重渲染 */
|
|
30
|
+
export function setLayoutMode(mode: LayoutMode) {
|
|
31
|
+
if (!LAYOUT_MODES.includes(mode) || currentLayoutMode === mode) return;
|
|
32
|
+
currentLayoutMode = mode;
|
|
33
|
+
if (typeof window !== 'undefined') {
|
|
34
|
+
try {
|
|
35
|
+
localStorage.setItem(LAYOUT_MODE_KEY, mode);
|
|
36
|
+
} catch {
|
|
37
|
+
// 忽略
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
EventBus.emit('layoutChange', mode);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function toggleLayoutMode(): LayoutMode {
|
|
44
|
+
const next: LayoutMode = currentLayoutMode === 'tabs' ? 'single' : 'tabs';
|
|
45
|
+
setLayoutMode(next);
|
|
46
|
+
return next;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 恢复默认布局(多标签页) */
|
|
50
|
+
export function resetLayoutMode() {
|
|
51
|
+
setLayoutMode(DEFAULT_LAYOUT_MODE);
|
|
52
|
+
}
|