feat: add admin-controlled toggles for header theme/language display in system settings
This commit is contained in:
parent
3e79e956f0
commit
09d6a7305e
|
|
@ -117,6 +117,8 @@ func GetStatus(c *gin.Context) {
|
|||
|
||||
// 模块管理配置
|
||||
"HeaderNavModules": common.OptionMap["HeaderNavModules"],
|
||||
"HeaderShowThemeToggle": common.OptionMap["HeaderShowThemeToggle"] != "false",
|
||||
"HeaderShowLanguageSelector": common.OptionMap["HeaderShowLanguageSelector"] != "false",
|
||||
"SidebarModulesByRole": common.OptionMap["SidebarModulesByRole"],
|
||||
|
||||
"oidc_enabled": system_setting.GetOIDCSettings().Enabled,
|
||||
|
|
|
|||
|
|
@ -290,6 +290,9 @@ func InitOptionMap() {
|
|||
common.OptionMap[k] = v
|
||||
}
|
||||
|
||||
common.OptionMap["HeaderShowThemeToggle"] = strconv.FormatBool(true)
|
||||
common.OptionMap["HeaderShowLanguageSelector"] = strconv.FormatBool(true)
|
||||
|
||||
common.OptionMapRWMutex.Unlock()
|
||||
loadOptionsFromDatabase()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ const ActionButtons = ({
|
|||
isLoading,
|
||||
isMobile,
|
||||
isSelfUseMode,
|
||||
showThemeToggle = true,
|
||||
showLanguageSelector = true,
|
||||
logout,
|
||||
navigate,
|
||||
t,
|
||||
|
|
@ -51,12 +53,16 @@ const ActionButtons = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
{showThemeToggle && (
|
||||
<ThemeToggle theme={theme} onThemeToggle={onThemeToggle} t={t} />
|
||||
)}
|
||||
|
||||
{showLanguageSelector && (
|
||||
<LanguageSelector
|
||||
currentLang={currentLang}
|
||||
onLanguageChange={onLanguageChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<UserArea
|
||||
userState={userState}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||
docsNav,
|
||||
isDemoSiteMode,
|
||||
isConsoleRoute,
|
||||
showThemeToggle,
|
||||
showLanguageSelector,
|
||||
theme,
|
||||
headerNavModules,
|
||||
pricingRequireAuth,
|
||||
|
|
@ -134,6 +136,8 @@ const HeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||
isLoading={isLoading}
|
||||
isMobile={isMobile}
|
||||
isSelfUseMode={isSelfUseMode}
|
||||
showThemeToggle={showThemeToggle}
|
||||
showLanguageSelector={showLanguageSelector}
|
||||
logout={logout}
|
||||
navigate={navigate}
|
||||
t={t}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,10 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||
|
||||
const isConsoleRoute = location.pathname.startsWith('/console');
|
||||
|
||||
// 读取顶栏主题切换和语言选择器开关配置
|
||||
const showThemeToggle = statusState?.status?.HeaderShowThemeToggle !== false;
|
||||
const showLanguageSelector = statusState?.status?.HeaderShowLanguageSelector !== false;
|
||||
|
||||
const theme = useTheme();
|
||||
const actualTheme = useActualTheme();
|
||||
const setTheme = useSetTheme();
|
||||
|
|
@ -251,6 +255,8 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||
docsNav,
|
||||
isDemoSiteMode,
|
||||
isConsoleRoute,
|
||||
showThemeToggle,
|
||||
showLanguageSelector,
|
||||
theme,
|
||||
drawerOpen,
|
||||
headerNavModules,
|
||||
|
|
|
|||
|
|
@ -5691,6 +5691,10 @@
|
|||
"支持模型": "Supported Models",
|
||||
"速率限制": "Rate Limits",
|
||||
"常见问题": "FAQ",
|
||||
"本页目录": "On this page"
|
||||
"本页目录": "On this page",
|
||||
"主题切换": "Theme Toggle",
|
||||
"顶栏显示主题切换按钮,允许用户切换亮色/暗色模式": "Show theme toggle in header bar, allowing users to switch between light/dark mode",
|
||||
"语言选择器": "Language Selector",
|
||||
"顶栏显示语言选择器,允许用户切换界面语言": "Show language selector in header bar, allowing users to switch interface language"
|
||||
}
|
||||
}
|
||||
|
|
@ -5550,6 +5550,10 @@
|
|||
"支持模型": "支持模型",
|
||||
"速率限制": "速率限制",
|
||||
"常见问题": "常见问题",
|
||||
"本页目录": "本页目录"
|
||||
"本页目录": "本页目录",
|
||||
"主题切换": "主题切换",
|
||||
"顶栏显示主题切换按钮,允许用户切换亮色/暗色模式": "顶栏显示主题切换按钮,允许用户切换亮色/暗色模式",
|
||||
"语言选择器": "语言选择器",
|
||||
"顶栏显示语言选择器,允许用户切换界面语言": "顶栏显示语言选择器,允许用户切换界面语言"
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,8 @@ export default function SettingsHeaderNavModules(props) {
|
|||
benchmark: true,
|
||||
docs: true,
|
||||
});
|
||||
const [showThemeToggle, setShowThemeToggle] = useState(true);
|
||||
const [showLanguageSelector, setShowLanguageSelector] = useState(true);
|
||||
|
||||
// 处理顶栏模块配置变更
|
||||
function handleHeaderNavModuleChange(moduleKey) {
|
||||
|
|
@ -127,6 +129,52 @@ export default function SettingsHeaderNavModules(props) {
|
|||
})();
|
||||
}
|
||||
|
||||
// 处理主题切换开关变更(即时保存)
|
||||
async function handleShowThemeToggleChange(checked) {
|
||||
setShowThemeToggle(checked);
|
||||
const key = 'HeaderShowThemeToggle';
|
||||
const value = String(checked);
|
||||
try {
|
||||
const res = await API.put('/api/option/', { key, value });
|
||||
if (res.data.success) {
|
||||
showSuccess(t('保存成功'));
|
||||
statusDispatch({
|
||||
type: 'set',
|
||||
payload: { ...statusState.status, [key]: value },
|
||||
});
|
||||
} else {
|
||||
showError(res.data.message);
|
||||
setShowThemeToggle(!checked);
|
||||
}
|
||||
} catch {
|
||||
showError(t('保存失败,请重试'));
|
||||
setShowThemeToggle(!checked);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理语言选择器开关变更(即时保存)
|
||||
async function handleShowLanguageSelectorChange(checked) {
|
||||
setShowLanguageSelector(checked);
|
||||
const key = 'HeaderShowLanguageSelector';
|
||||
const value = String(checked);
|
||||
try {
|
||||
const res = await API.put('/api/option/', { key, value });
|
||||
if (res.data.success) {
|
||||
showSuccess(t('保存成功'));
|
||||
statusDispatch({
|
||||
type: 'set',
|
||||
payload: { ...statusState.status, [key]: value },
|
||||
});
|
||||
} else {
|
||||
showError(res.data.message);
|
||||
setShowLanguageSelector(!checked);
|
||||
}
|
||||
} catch {
|
||||
showError(t('保存失败,请重试'));
|
||||
setShowLanguageSelector(!checked);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理模糊价格开关变更(即时保存)
|
||||
function handleBlurPricingChange(moduleKey) {
|
||||
return async (checked) => {
|
||||
|
|
@ -224,6 +272,17 @@ export default function SettingsHeaderNavModules(props) {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
// 从 props.options 中获取主题切换和语言选择器设置
|
||||
if (props.options) {
|
||||
const themeVal = props.options.HeaderShowThemeToggle;
|
||||
if (themeVal !== undefined && themeVal !== null) {
|
||||
setShowThemeToggle(themeVal === 'true' || themeVal === true);
|
||||
}
|
||||
const langVal = props.options.HeaderShowLanguageSelector;
|
||||
if (langVal !== undefined && langVal !== null) {
|
||||
setShowLanguageSelector(langVal === 'true' || langVal === true);
|
||||
}
|
||||
}
|
||||
// 从 props.options 中获取配置
|
||||
if (props.options && props.options.HeaderNavModules) {
|
||||
try {
|
||||
|
|
@ -547,6 +606,118 @@ export default function SettingsHeaderNavModules(props) {
|
|||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
|
||||
{/* 主题切换开关 */}
|
||||
<Col xs={24} sm={12} md={6} lg={6} xl={6}>
|
||||
<Card
|
||||
className='transition-shadow hover:shadow-md'
|
||||
style={{
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--semi-color-border)',
|
||||
transition: 'all 0.2s ease',
|
||||
background: 'var(--semi-color-bg-1)',
|
||||
minHeight: '80px',
|
||||
}}
|
||||
bodyStyle={{ padding: '16px' }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, textAlign: 'left' }}>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: '600',
|
||||
fontSize: '14px',
|
||||
color: 'var(--semi-color-text-0)',
|
||||
marginBottom: '4px',
|
||||
}}
|
||||
>
|
||||
{t('主题切换')}
|
||||
</div>
|
||||
<Text
|
||||
type='secondary'
|
||||
size='small'
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
color: 'var(--semi-color-text-2)',
|
||||
lineHeight: '1.4',
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
{t('顶栏显示主题切换按钮,允许用户切换亮色/暗色模式')}
|
||||
</Text>
|
||||
</div>
|
||||
<div style={{ marginLeft: '16px' }}>
|
||||
<Switch
|
||||
checked={showThemeToggle}
|
||||
onChange={handleShowThemeToggleChange}
|
||||
size='default'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* 语言选择器开关 */}
|
||||
<Col xs={24} sm={12} md={6} lg={6} xl={6}>
|
||||
<Card
|
||||
className='transition-shadow hover:shadow-md'
|
||||
style={{
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--semi-color-border)',
|
||||
transition: 'all 0.2s ease',
|
||||
background: 'var(--semi-color-bg-1)',
|
||||
minHeight: '80px',
|
||||
}}
|
||||
bodyStyle={{ padding: '16px' }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, textAlign: 'left' }}>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: '600',
|
||||
fontSize: '14px',
|
||||
color: 'var(--semi-color-text-0)',
|
||||
marginBottom: '4px',
|
||||
}}
|
||||
>
|
||||
{t('语言选择器')}
|
||||
</div>
|
||||
<Text
|
||||
type='secondary'
|
||||
size='small'
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
color: 'var(--semi-color-text-2)',
|
||||
lineHeight: '1.4',
|
||||
display: 'block',
|
||||
}}
|
||||
>
|
||||
{t('顶栏显示语言选择器,允许用户切换界面语言')}
|
||||
</Text>
|
||||
</div>
|
||||
<div style={{ marginLeft: '16px' }}>
|
||||
<Switch
|
||||
checked={showLanguageSelector}
|
||||
onChange={handleShowLanguageSelectorChange}
|
||||
size='default'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<div
|
||||
|
|
|
|||
Loading…
Reference in New Issue