mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-03 08:57:15 +00:00
feat(settings): sidebar submenu nav for settings and xray with icon tabs
Settings and Xray Configs are now expandable sidebar submenus that list their sections; clicking a section opens it via the URL hash (e.g. #general, #basic) and the in-page top tab bar is removed on both pages. Within each section the collapse groups become horizontal tabs, each with an icon; on mobile only the icon shows with the label in a tooltip, via a shared catTabLabel helper used by both settings and xray. Subscription Formats: the nested collapses in Fragment/Noises/Mux/Direct are replaced with a cleaner layout - framed field groups, and each noise is a card with a delete button plus a dashed add button. Xray: the Reset to Default button is now a solid danger button so its hover state is visible.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
@@ -12,17 +13,8 @@ import {
|
||||
Row,
|
||||
Space,
|
||||
Spin,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
CloudServerOutlined,
|
||||
CodeOutlined,
|
||||
MessageOutlined,
|
||||
SafetyOutlined,
|
||||
SettingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import { HttpUtil, PromiseUtil } from '@/utils';
|
||||
import { setMessageInstance } from '@/utils/messageBus';
|
||||
@@ -44,15 +36,6 @@ interface ApiMsg {
|
||||
|
||||
const tabSlugs = ['general', 'security', 'telegram', 'subscription', 'subscription-formats'];
|
||||
|
||||
function slugToKey(slug: string): string {
|
||||
const i = tabSlugs.indexOf(slug);
|
||||
return i >= 0 ? String(i + 1) : '1';
|
||||
}
|
||||
|
||||
function keyToSlug(key: string): string {
|
||||
return tabSlugs[Number(key) - 1] || tabSlugs[0];
|
||||
}
|
||||
|
||||
function isIp(h: string): boolean {
|
||||
if (typeof h !== 'string') return false;
|
||||
const v4 = h.split('.');
|
||||
@@ -108,21 +91,9 @@ export default function SettingsPage() {
|
||||
}, []);
|
||||
|
||||
const [alertVisible, setAlertVisible] = useState(true);
|
||||
const [activeTabKey, setActiveTabKey] = useState<string>(() => slugToKey(window.location.hash.slice(1)));
|
||||
|
||||
useEffect(() => {
|
||||
const onHashChange = () => setActiveTabKey(slugToKey(window.location.hash.slice(1)));
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
return () => window.removeEventListener('hashchange', onHashChange);
|
||||
}, []);
|
||||
|
||||
function onTabChange(key: string) {
|
||||
setActiveTabKey(key);
|
||||
const slug = keyToSlug(key);
|
||||
if (window.location.hash !== `#${slug}`) {
|
||||
history.replaceState(null, '', `#${slug}`);
|
||||
}
|
||||
}
|
||||
const location = useLocation();
|
||||
const slug = location.hash.replace(/^#/, '');
|
||||
const activeSlug = tabSlugs.includes(slug) ? slug : 'general';
|
||||
|
||||
function rebuildUrlAfterRestart(): string {
|
||||
const { webDomain, webPort, webBasePath, webCertFile, webKeyFile } = allSetting;
|
||||
@@ -222,58 +193,15 @@ export default function SettingsPage() {
|
||||
return classes.join(' ');
|
||||
}, [isDark, isUltra]);
|
||||
|
||||
const tabItems = useMemo(() => {
|
||||
const items: { key: string; label: React.ReactNode; children: React.ReactNode }[] = [
|
||||
{
|
||||
key: '1',
|
||||
label: (
|
||||
<Tooltip title={isMobile ? t('pages.settings.panelSettings') : null}>
|
||||
<span><SettingOutlined />{!isMobile && <> {t('pages.settings.panelSettings')}</>}</span>
|
||||
</Tooltip>
|
||||
),
|
||||
children: <GeneralTab allSetting={allSetting} updateSetting={updateSetting} />,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: (
|
||||
<Tooltip title={isMobile ? t('pages.settings.securitySettings') : null}>
|
||||
<span><SafetyOutlined />{!isMobile && <> {t('pages.settings.securitySettings')}</>}</span>
|
||||
</Tooltip>
|
||||
),
|
||||
children: <SecurityTab allSetting={allSetting} updateSetting={updateSetting} />,
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: (
|
||||
<Tooltip title={isMobile ? t('pages.settings.TGBotSettings') : null}>
|
||||
<span><MessageOutlined />{!isMobile && <> {t('pages.settings.TGBotSettings')}</>}</span>
|
||||
</Tooltip>
|
||||
),
|
||||
children: <TelegramTab allSetting={allSetting} updateSetting={updateSetting} />,
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
label: (
|
||||
<Tooltip title={isMobile ? t('pages.settings.subSettings') : null}>
|
||||
<span><CloudServerOutlined />{!isMobile && <> {t('pages.settings.subSettings')}</>}</span>
|
||||
</Tooltip>
|
||||
),
|
||||
children: <SubscriptionGeneralTab allSetting={allSetting} updateSetting={updateSetting} />,
|
||||
},
|
||||
];
|
||||
if (allSetting.subJsonEnable || allSetting.subClashEnable) {
|
||||
items.push({
|
||||
key: '5',
|
||||
label: (
|
||||
<Tooltip title={isMobile ? `${t('pages.settings.subSettings')} (Formats)` : null}>
|
||||
<span><CodeOutlined />{!isMobile && <> {t('pages.settings.subSettings')} (Formats)</>}</span>
|
||||
</Tooltip>
|
||||
),
|
||||
children: <SubscriptionFormatsTab allSetting={allSetting} updateSetting={updateSetting} />,
|
||||
});
|
||||
const categoryBody = useMemo(() => {
|
||||
switch (activeSlug) {
|
||||
case 'security': return <SecurityTab allSetting={allSetting} updateSetting={updateSetting} />;
|
||||
case 'telegram': return <TelegramTab allSetting={allSetting} updateSetting={updateSetting} />;
|
||||
case 'subscription': return <SubscriptionGeneralTab allSetting={allSetting} updateSetting={updateSetting} />;
|
||||
case 'subscription-formats': return <SubscriptionFormatsTab allSetting={allSetting} updateSetting={updateSetting} />;
|
||||
default: return <GeneralTab allSetting={allSetting} updateSetting={updateSetting} />;
|
||||
}
|
||||
return items;
|
||||
}, [allSetting, updateSetting, isMobile, t]);
|
||||
}, [activeSlug, allSetting, updateSetting]);
|
||||
|
||||
return (
|
||||
<ConfigProvider theme={antdThemeConfig}>
|
||||
@@ -331,12 +259,7 @@ export default function SettingsPage() {
|
||||
|
||||
<Col span={24}>
|
||||
<Card hoverable>
|
||||
<Tabs
|
||||
activeKey={activeTabKey}
|
||||
onChange={onTabChange}
|
||||
className={isMobile ? 'icons-only' : ''}
|
||||
items={tabItems}
|
||||
/>
|
||||
{categoryBody}
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
Reference in New Issue
Block a user