mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
chore(frontend): adopt antd v6 api updates
Sweep deprecated props across the React tree: - Modal: destroyOnClose -> destroyOnHidden, maskClosable -> mask.closable - Space: direction -> orientation (or removed when redundant) - Input.Group compact -> Space.Compact block - Drawer: width -> size - Spin: tip -> description - Progress: trailColor -> railColor - Alert: message -> title - Popover: overlayClassName -> rootClassName - BackTop -> FloatButton.BackTop Also refresh dashboard theming for v6: rename dark/ultra Layout and Menu tokens (siderBg, darkItemBg, darkSubMenuItemBg, darkPopupBg), tweak gauge size/stroke, add font-size overrides for Statistic and Progress so the overview numbers stay legible under v6 defaults.
This commit is contained in:
parent
d6f42b3395
commit
7a4317086b
45 changed files with 163 additions and 136 deletions
|
|
@ -199,7 +199,7 @@ export default function AppSidebar({ basePath = '', requestUri = '' }: AppSideba
|
|||
closable={false}
|
||||
open={drawerOpen}
|
||||
rootClassName={currentTheme}
|
||||
width="min(82vw, 320px)"
|
||||
size="min(82vw, 320px)"
|
||||
styles={{
|
||||
wrapper: { padding: 0 },
|
||||
body: { padding: 0, display: 'flex', flexDirection: 'column', height: '100%' },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,25 @@
|
|||
.ant-statistic-content {
|
||||
font-size: 16px;
|
||||
font-size: 15px !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
|
||||
.ant-statistic-content-value,
|
||||
.ant-statistic-content-prefix,
|
||||
.ant-statistic-content-suffix {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
.ant-statistic-content-prefix {
|
||||
margin-inline-end: 6px !important;
|
||||
}
|
||||
|
||||
.ant-statistic-content-prefix .anticon {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
.ant-statistic-title {
|
||||
font-size: 12px !important;
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
body.dark .ant-statistic-content {
|
||||
|
|
|
|||
|
|
@ -55,11 +55,11 @@ export default function PromptModal({
|
|||
title={title}
|
||||
okText={okText}
|
||||
cancelText="Cancel"
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
confirmLoading={loading}
|
||||
onOk={() => onConfirm(value)}
|
||||
onCancel={onClose}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
{type === 'textarea' ? (
|
||||
<Input.TextArea
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ export default function TextModal({ open, onClose, title, content, fileName = ''
|
|||
open={open}
|
||||
title={title}
|
||||
onCancel={onClose}
|
||||
closable
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
footer={(
|
||||
<>
|
||||
{fileName && (
|
||||
|
|
|
|||
|
|
@ -42,24 +42,32 @@ const ULTRA_DARK_TOKENS = {
|
|||
colorBgElevated: '#141414',
|
||||
};
|
||||
const DARK_LAYOUT_TOKENS = {
|
||||
colorBgHeader: '#252526',
|
||||
colorBgTrigger: '#333333',
|
||||
colorBgBody: '#1e1e1e',
|
||||
bodyBg: '#1e1e1e',
|
||||
headerBg: '#252526',
|
||||
headerColor: '#ffffff',
|
||||
footerBg: '#1e1e1e',
|
||||
siderBg: '#252526',
|
||||
triggerBg: '#333333',
|
||||
triggerColor: '#ffffff',
|
||||
};
|
||||
const ULTRA_DARK_LAYOUT_TOKENS = {
|
||||
colorBgHeader: '#0a0a0a',
|
||||
colorBgTrigger: '#141414',
|
||||
colorBgBody: '#000',
|
||||
bodyBg: '#000',
|
||||
headerBg: '#0a0a0a',
|
||||
headerColor: '#ffffff',
|
||||
footerBg: '#000',
|
||||
siderBg: '#0a0a0a',
|
||||
triggerBg: '#141414',
|
||||
triggerColor: '#ffffff',
|
||||
};
|
||||
const DARK_MENU_TOKENS = {
|
||||
colorItemBg: '#252526',
|
||||
colorSubItemBg: '#1e1e1e',
|
||||
menuSubMenuBg: '#252526',
|
||||
darkItemBg: '#252526',
|
||||
darkSubMenuItemBg: '#1e1e1e',
|
||||
darkPopupBg: '#252526',
|
||||
};
|
||||
const ULTRA_DARK_MENU_TOKENS = {
|
||||
colorItemBg: '#0a0a0a',
|
||||
colorSubItemBg: '#000',
|
||||
menuSubMenuBg: '#0a0a0a',
|
||||
darkItemBg: '#0a0a0a',
|
||||
darkSubMenuItemBg: '#000',
|
||||
darkPopupBg: '#0a0a0a',
|
||||
};
|
||||
|
||||
export function buildAntdThemeConfig(isDark: boolean, isUltra: boolean): ThemeConfig {
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ export default function ClientBulkAddModal({
|
|||
okText={t('create')}
|
||||
cancelText={t('close')}
|
||||
confirmLoading={saving}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
width={640}
|
||||
onOk={submit}
|
||||
onCancel={() => onOpenChange(false)}
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ export default function ClientFormModal({
|
|||
<Modal
|
||||
open={open}
|
||||
title={isEdit ? t('pages.clients.editTitle') : t('pages.clients.addTitle')}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
okText={isEdit ? t('save') : t('create')}
|
||||
cancelText={t('cancel')}
|
||||
okButtonProps={{ loading: submitting }}
|
||||
|
|
|
|||
|
|
@ -626,7 +626,7 @@ export default function ClientsPage() {
|
|||
|
||||
<Layout className="content-shell">
|
||||
<Layout.Content id="content-layout" className="content-area">
|
||||
<Spin spinning={!fetched} delay={200} tip={t('loading')} size="large">
|
||||
<Spin spinning={!fetched} delay={200} description={t('loading')} size="large">
|
||||
{!fetched ? (
|
||||
<div className="loading-spacer" />
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -648,14 +648,14 @@ export default function InboundFormModal({
|
|||
const sniffingFallback = () => inboundRef.current?.sniffing?.toJson?.() || {};
|
||||
const streamFallback = () => inboundRef.current?.stream?.toJson?.() || {};
|
||||
|
||||
const parseAdvancedSliceWithLabel = (rawText: string, fallback: unknown, label: string) => {
|
||||
const parseAdvancedSliceWithLabel = useCallback((rawText: string, fallback: unknown, label: string) => {
|
||||
try {
|
||||
return parseAdvancedSliceOrFallback(rawText, fallback);
|
||||
} catch (e) {
|
||||
message.error(`${label} JSON invalid: ${(e as Error).message}`);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const compactAdvancedJson = (raw: string, fallback: string, label: string) => {
|
||||
try {
|
||||
|
|
@ -696,7 +696,7 @@ export default function InboundFormModal({
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
}, [t, refresh]);
|
||||
}, [t, refresh, parseAdvancedSliceWithLabel]);
|
||||
|
||||
const handleTabChange = (next: string) => {
|
||||
if (activeTabKey === 'advanced' && next !== 'advanced') {
|
||||
|
|
@ -886,16 +886,15 @@ export default function InboundFormModal({
|
|||
}
|
||||
}, [canEnableStream, t, mode, dbInbound, isFallbackHost, saveFallbacks, onSaved, onClose]);
|
||||
|
||||
const protocolSnapshot = inboundRef.current?.protocol;
|
||||
const streamSnapshot = JSON.stringify(inboundRef.current?.stream?.toJson?.() || {});
|
||||
const sniffingSnapshot = JSON.stringify(inboundRef.current?.sniffing?.toJson?.() || {});
|
||||
const settingsSnapshot = JSON.stringify(inboundRef.current?.settings?.toJson?.() || {});
|
||||
|
||||
useEffect(() => {
|
||||
if (!inboundRef.current) return;
|
||||
(['stream', 'sniffing', 'settings'] as const).forEach(stampAdvancedTextFor);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
inboundRef.current?.protocol,
|
||||
JSON.stringify(inboundRef.current?.stream?.toJson?.() || {}),
|
||||
JSON.stringify(inboundRef.current?.sniffing?.toJson?.() || {}),
|
||||
JSON.stringify(inboundRef.current?.settings?.toJson?.() || {}),
|
||||
]);
|
||||
}, [protocolSnapshot, streamSnapshot, sniffingSnapshot, settingsSnapshot, stampAdvancedTextFor]);
|
||||
|
||||
const title = mode === 'edit' ? t('pages.inbounds.modifyInbound') : t('pages.inbounds.addInbound');
|
||||
const okText = mode === 'edit' ? t('pages.clients.submitEdit') : t('create');
|
||||
|
|
@ -998,7 +997,7 @@ export default function InboundFormModal({
|
|||
<div key={record.rowKey} style={{ border: '1px solid var(--app-border-tertiary)', borderRadius: 6, padding: '10px 12px', marginBottom: 8 }}>
|
||||
<Row gutter={8} align="middle" wrap={false}>
|
||||
<Col flex="none">
|
||||
<Space direction="vertical" size={2}>
|
||||
<Space orientation="vertical" size={2}>
|
||||
<Button size="small" type="text" disabled={index === 0} onClick={() => moveFallback(index, -1)}>
|
||||
<CaretUpOutlined />
|
||||
</Button>
|
||||
|
|
@ -1146,7 +1145,7 @@ export default function InboundFormModal({
|
|||
</Form.Item>
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.settings.accounts || []).map((account: any, idx: number) => (
|
||||
<Input.Group key={idx} compact className="mb-8">
|
||||
<Space.Compact key={idx} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={account.user}
|
||||
addonBefore={String(idx + 1)} placeholder="Username"
|
||||
onChange={(e) => { account.user = e.target.value; refresh(); }} />
|
||||
|
|
@ -1155,7 +1154,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.settings.delAccount(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
{ib.protocol === Protocols.HTTP && (
|
||||
|
|
@ -1208,7 +1207,7 @@ export default function InboundFormModal({
|
|||
{(ib.settings.portMap || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.settings.portMap as { name: string; value: string }[]).map((pm, idx) => (
|
||||
<Input.Group key={`pm-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`pm-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '30%' }} value={pm.name} placeholder="5555" addonBefore={String(idx + 1)}
|
||||
onChange={(e) => { pm.name = e.target.value; refresh(); }} />
|
||||
<Input style={{ width: '60%' }} value={pm.value} placeholder="1.1.1.1:7777"
|
||||
|
|
@ -1216,7 +1215,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.settings.removePortMap(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1405,7 +1404,7 @@ export default function InboundFormModal({
|
|||
{(ib.stream.tcp.request.headers || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.tcp.request.headers as { name: string; value: string }[]).map((h, idx) => (
|
||||
<Input.Group key={`tcp-rh-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`tcp-rh-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={h.name} addonBefore={String(idx + 1)}
|
||||
placeholder={t('pages.inbounds.stream.general.name')}
|
||||
onChange={(e) => { h.name = e.target.value; refresh(); }} />
|
||||
|
|
@ -1415,7 +1414,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.stream.tcp.request.removeHeader(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1440,7 +1439,7 @@ export default function InboundFormModal({
|
|||
{(ib.stream.tcp.response.headers || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.tcp.response.headers as { name: string; value: string }[]).map((h, idx) => (
|
||||
<Input.Group key={`tcp-rsh-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`tcp-rsh-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={h.name} addonBefore={String(idx + 1)}
|
||||
placeholder={t('pages.inbounds.stream.general.name')}
|
||||
onChange={(e) => { h.name = e.target.value; refresh(); }} />
|
||||
|
|
@ -1450,7 +1449,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.stream.tcp.response.removeHeader(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1482,7 +1481,7 @@ export default function InboundFormModal({
|
|||
{(ib.stream.ws.headers || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.ws.headers as { name: string; value: string }[]).map((h, idx) => (
|
||||
<Input.Group key={`ws-h-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`ws-h-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={h.name} addonBefore={String(idx + 1)}
|
||||
placeholder={t('pages.inbounds.stream.general.name')}
|
||||
onChange={(e) => { h.name = e.target.value; refresh(); }} />
|
||||
|
|
@ -1492,7 +1491,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.stream.ws.removeHeader(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1518,7 +1517,7 @@ export default function InboundFormModal({
|
|||
{(ib.stream.httpupgrade.headers || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.httpupgrade.headers as { name: string; value: string }[]).map((h, idx) => (
|
||||
<Input.Group key={`hu-h-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`hu-h-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={h.name} addonBefore={String(idx + 1)}
|
||||
placeholder={t('pages.inbounds.stream.general.name')}
|
||||
onChange={(e) => { h.name = e.target.value; refresh(); }} />
|
||||
|
|
@ -1528,7 +1527,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.stream.httpupgrade.removeHeader(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1545,7 +1544,7 @@ export default function InboundFormModal({
|
|||
{(ib.stream.xhttp.headers || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.xhttp.headers as { name: string; value: string }[]).map((h, idx) => (
|
||||
<Input.Group key={`xh-h-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`xh-h-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={h.name} addonBefore={String(idx + 1)}
|
||||
placeholder={t('pages.inbounds.stream.general.name')}
|
||||
onChange={(e) => { h.name = e.target.value; refresh(); }} />
|
||||
|
|
@ -1555,7 +1554,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.stream.xhttp.removeHeader(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1652,7 +1651,7 @@ export default function InboundFormModal({
|
|||
{externalProxyOn && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.externalProxy as { forceTls: string; dest: string; port: number; remark: string }[]).map((row, idx) => (
|
||||
<Input.Group key={`ep-${idx}`} compact style={{ margin: '8px 0' }}>
|
||||
<Space.Compact key={`ep-${idx}`} style={{ margin: '8px 0' }} block>
|
||||
<Tooltip title="Force TLS">
|
||||
<Select value={row.forceTls} style={{ width: '20%' }} onChange={(v) => { row.forceTls = v; refresh(); }}>
|
||||
<Select.Option value="same">{t('pages.inbounds.same')}</Select.Option>
|
||||
|
|
@ -1669,7 +1668,7 @@ export default function InboundFormModal({
|
|||
<Input style={{ width: '35%' }} value={row.remark} placeholder={t('pages.inbounds.remark')}
|
||||
onChange={(e) => { row.remark = e.target.value; refresh(); }}
|
||||
addonAfter={<MinusOutlined onClick={() => { ib.stream.externalProxy.splice(idx, 1); refresh(); }} />} />
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1762,7 +1761,7 @@ export default function InboundFormModal({
|
|||
{(ib.stream.hysteria.masquerade.headers || []).length > 0 && (
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{(ib.stream.hysteria.masquerade.headers as { name: string; value: string }[]).map((h, idx) => (
|
||||
<Input.Group key={`mh-${idx}`} compact className="mb-8">
|
||||
<Space.Compact key={`mh-${idx}`} className="mb-8" block>
|
||||
<Input style={{ width: '45%' }} value={h.name} addonBefore={String(idx + 1)} placeholder="Name"
|
||||
onChange={(e) => { h.name = e.target.value; refresh(); }} />
|
||||
<Input style={{ width: '45%' }} value={h.value} placeholder="Value"
|
||||
|
|
@ -1770,7 +1769,7 @@ export default function InboundFormModal({
|
|||
<Button onClick={() => { ib.stream.hysteria.masquerade.removeHeader(idx); refresh(); }}>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
))}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
|
@ -1808,14 +1807,14 @@ export default function InboundFormModal({
|
|||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="Min/Max Version">
|
||||
<Input.Group compact>
|
||||
<Space.Compact block>
|
||||
<Select value={ib.stream.tls.minVersion} style={{ width: '50%' }} onChange={(v) => { ib.stream.tls.minVersion = v; refresh(); }}>
|
||||
{TLS_VERSIONS.map((v) => <Select.Option key={v} value={v}>{v}</Select.Option>)}
|
||||
</Select>
|
||||
<Select value={ib.stream.tls.maxVersion} style={{ width: '50%' }} onChange={(v) => { ib.stream.tls.maxVersion = v; refresh(); }}>
|
||||
{TLS_VERSIONS.map((v) => <Select.Option key={v} value={v}>{v}</Select.Option>)}
|
||||
</Select>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
<Form.Item label="uTLS">
|
||||
<Select value={ib.stream.tls.settings.fingerprint} style={{ width: '100%' }} onChange={(v) => { ib.stream.tls.settings.fingerprint = v; refresh(); }}>
|
||||
|
|
@ -2085,11 +2084,11 @@ export default function InboundFormModal({
|
|||
okText={okText}
|
||||
cancelText={t('close')}
|
||||
confirmLoading={saving}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
width={780}
|
||||
onOk={submit}
|
||||
onCancel={onClose}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
<Tabs activeKey={activeTabKey} onChange={handleTabChange} items={tabItems} />
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -882,7 +882,7 @@ export default function InboundInfoModal({
|
|||
tabItems.push({ key: 'inbound', label: t('pages.xray.rules.inbound'), children: inboundTab });
|
||||
|
||||
return (
|
||||
<Modal open={open} onCancel={onClose} title={t('pages.inbounds.inboundData')} footer={null} width={640} destroyOnClose>
|
||||
<Modal open={open} onCancel={onClose} title={t('pages.inbounds.inboundData')} footer={null} width={640} destroyOnHidden>
|
||||
<Tabs activeKey={activeTab} onChange={setActiveTab} items={tabItems} />
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -245,12 +245,12 @@ export default function InboundList({
|
|||
[dbInbounds],
|
||||
);
|
||||
|
||||
const sorterFor = (key: SortKey) => ({
|
||||
const sorterFor = useCallback((key: SortKey) => ({
|
||||
sorter: true as const,
|
||||
showSorterTooltip: false,
|
||||
sortOrder: sortKey === key ? sortOrder : null,
|
||||
sortDirections: ['ascend' as const, 'descend' as const],
|
||||
});
|
||||
}), [sortKey, sortOrder]);
|
||||
|
||||
const columns: TableColumnType<DBInboundRecord>[] = useMemo(() => {
|
||||
const cols: TableColumnType<DBInboundRecord>[] = [
|
||||
|
|
@ -474,7 +474,7 @@ export default function InboundList({
|
|||
);
|
||||
|
||||
return cols;
|
||||
}, [t, hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable]);
|
||||
}, [t, hasAnyRemark, hasActiveNode, nodesById, clientCount, subEnable, expireDiff, trafficDiff, datepicker, onRowAction, onSwitchEnable, sorterFor]);
|
||||
|
||||
const paginationFor = (rows: DBInboundRecord[]) => {
|
||||
const size = pageSize > 0 ? pageSize : rows.length || 1;
|
||||
|
|
@ -497,7 +497,7 @@ export default function InboundList({
|
|||
<Card
|
||||
hoverable
|
||||
title={(
|
||||
<Space direction="horizontal">
|
||||
<Space>
|
||||
<Button type="primary" onClick={onAddInbound} icon={<PlusOutlined />}>
|
||||
{!isMobile && t('pages.inbounds.addInbound')}
|
||||
</Button>
|
||||
|
|
@ -509,7 +509,7 @@ export default function InboundList({
|
|||
</Space>
|
||||
)}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
||||
{isMobile ? (
|
||||
<div className="inbound-cards">
|
||||
{sortedInbounds.length === 0 ? (
|
||||
|
|
@ -571,7 +571,7 @@ export default function InboundList({
|
|||
centered
|
||||
title={statsRecord ? `#${statsRecord.id} ${statsRecord.remark || ''}`.trim() : ''}
|
||||
onCancel={() => setStatsRecord(null)}
|
||||
destroyOnClose
|
||||
destroyOnHidden
|
||||
>
|
||||
{statsRecord && (
|
||||
<div className="card-stats">
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ export default function InboundsPage() {
|
|||
|
||||
<Layout className="content-shell">
|
||||
<Layout.Content id="content-layout" className="content-area">
|
||||
<Spin spinning={!fetched} delay={200} tip="Loading…" size="large">
|
||||
<Spin spinning={!fetched} delay={200} description="Loading…" size="large">
|
||||
{!fetched ? (
|
||||
<div className="loading-spacer" />
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ export default function QrCodeModal({
|
|||
}));
|
||||
|
||||
return (
|
||||
<Modal open={open} onCancel={onClose} title={t('qrCode')} footer={null} width={420} destroyOnClose>
|
||||
<Modal open={open} onCancel={onClose} title={t('qrCode')} footer={null} width={420} destroyOnHidden>
|
||||
{dbInbound && (
|
||||
<Collapse
|
||||
ghost
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ export default function BackupModal({ open, basePath: _basePath, onClose, onBusy
|
|||
<Modal
|
||||
open={open}
|
||||
title={t('pages.index.backupTitle')}
|
||||
closable
|
||||
footer={null}
|
||||
onCancel={onClose}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ export default function CustomGeoSection({ active }: CustomGeoSectionProps) {
|
|||
type="info"
|
||||
showIcon
|
||||
className="mb-10"
|
||||
message={t('pages.index.customGeoRoutingHint')}
|
||||
title={t('pages.index.customGeoRoutingHint')}
|
||||
/>
|
||||
|
||||
<div className="toolbar">
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ export default function IndexPage() {
|
|||
<Spin
|
||||
spinning={loading || !fetched}
|
||||
delay={200}
|
||||
tip={loading ? loadingTip : t('loading')}
|
||||
description={loading ? loadingTip : t('loading')}
|
||||
size="large"
|
||||
>
|
||||
{!fetched ? (
|
||||
|
|
@ -450,7 +450,6 @@ export default function IndexPage() {
|
|||
title={t('pages.index.config')}
|
||||
width={isMobile ? '100%' : 900}
|
||||
style={isMobile ? { top: 20, maxWidth: 'calc(100vw - 16px)' } : undefined}
|
||||
closable
|
||||
onCancel={() => setConfigTextOpen(false)}
|
||||
footer={[
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Checkbox, Form, Input, Modal, Select } from 'antd';
|
||||
import { Button, Checkbox, Form, Modal, Select, Space } from 'antd';
|
||||
import { DownloadOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
|
||||
import { HttpUtil, FileManager, PromiseUtil } from '@/utils';
|
||||
|
|
@ -107,7 +107,6 @@ export default function LogModal({ open, onClose }: LogModalProps) {
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
closable
|
||||
footer={null}
|
||||
width={isMobile ? '100vw' : 800}
|
||||
className={isMobile ? 'logmodal-mobile' : undefined}
|
||||
|
|
@ -116,7 +115,7 @@ export default function LogModal({ open, onClose }: LogModalProps) {
|
|||
>
|
||||
<Form layout="inline" className="log-toolbar">
|
||||
<Form.Item>
|
||||
<Input.Group compact>
|
||||
<Space.Compact>
|
||||
<Select value={rows} size="small" style={{ width: 70 }} onChange={setRows}>
|
||||
<Select.Option value="10">10</Select.Option>
|
||||
<Select.Option value="20">20</Select.Option>
|
||||
|
|
@ -131,7 +130,7 @@ export default function LogModal({ open, onClose }: LogModalProps) {
|
|||
<Select.Option value="warning">Warning</Select.Option>
|
||||
<Select.Option value="err">Error</Select.Option>
|
||||
</Select>
|
||||
</Input.Group>
|
||||
</Space.Compact>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Checkbox checked={syslog} onChange={(e) => setSyslog(e.target.checked)}>
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ export default function PanelUpdateModal({ open, info, onClose, onBusy }: PanelU
|
|||
<Modal
|
||||
open={open}
|
||||
title={t('pages.index.updatePanel')}
|
||||
closable
|
||||
footer={null}
|
||||
onCancel={onClose}
|
||||
>
|
||||
|
|
@ -80,7 +79,7 @@ export default function PanelUpdateModal({ open, info, onClose, onBusy }: PanelU
|
|||
<Alert
|
||||
type="warning"
|
||||
className="mb-12"
|
||||
message={t('pages.index.panelUpdateDesc')}
|
||||
title={t('pages.index.panelUpdateDesc')}
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.status-card .ant-progress-text {
|
||||
font-size: 14px !important;
|
||||
.status-card .ant-progress-text,
|
||||
.status-card .ant-progress-indicator {
|
||||
font-size: 12px !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Card, Col, Progress, Row, Tooltip } from 'antd';
|
|||
import { AreaChartOutlined } from '@ant-design/icons';
|
||||
|
||||
import { CPUFormatter, SizeFormatter } from '@/utils';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import type { Status } from '@/models/status';
|
||||
import './StatusCard.css';
|
||||
|
||||
|
|
@ -11,11 +12,14 @@ interface StatusCardProps {
|
|||
isMobile: boolean;
|
||||
}
|
||||
|
||||
const TRAIL_COLOR = 'rgba(128, 128, 128, 0.25)';
|
||||
|
||||
export default function StatusCard({ status, isMobile }: StatusCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const gaugeSize = isMobile ? 60 : 70;
|
||||
const { isDark, isUltra } = useTheme();
|
||||
const gaugeSize = isMobile ? 60 : 90;
|
||||
const strokeWidth = isMobile ? 7 : 5;
|
||||
const railColor = isDark
|
||||
? isUltra ? 'rgba(255, 255, 255, 0.1)' : 'rgba(255, 255, 255, 0.16)'
|
||||
: 'rgba(0, 0, 0, 0.08)';
|
||||
|
||||
return (
|
||||
<Card hoverable className="status-card">
|
||||
|
|
@ -27,7 +31,8 @@ export default function StatusCard({ status, isMobile }: StatusCardProps) {
|
|||
type="dashboard"
|
||||
status="normal"
|
||||
strokeColor={status.cpu.color}
|
||||
trailColor={TRAIL_COLOR}
|
||||
railColor={railColor}
|
||||
strokeWidth={strokeWidth}
|
||||
percent={status.cpu.percent}
|
||||
size={gaugeSize}
|
||||
/>
|
||||
|
|
@ -56,7 +61,8 @@ export default function StatusCard({ status, isMobile }: StatusCardProps) {
|
|||
type="dashboard"
|
||||
status="normal"
|
||||
strokeColor={status.mem.color}
|
||||
trailColor={TRAIL_COLOR}
|
||||
railColor={railColor}
|
||||
strokeWidth={strokeWidth}
|
||||
percent={status.mem.percent}
|
||||
size={gaugeSize}
|
||||
/>
|
||||
|
|
@ -75,7 +81,8 @@ export default function StatusCard({ status, isMobile }: StatusCardProps) {
|
|||
type="dashboard"
|
||||
status="normal"
|
||||
strokeColor={status.swap.color}
|
||||
trailColor={TRAIL_COLOR}
|
||||
railColor={railColor}
|
||||
strokeWidth={strokeWidth}
|
||||
percent={status.swap.percent}
|
||||
size={gaugeSize}
|
||||
/>
|
||||
|
|
@ -90,7 +97,8 @@ export default function StatusCard({ status, isMobile }: StatusCardProps) {
|
|||
type="dashboard"
|
||||
status="normal"
|
||||
strokeColor={status.disk.color}
|
||||
trailColor={TRAIL_COLOR}
|
||||
railColor={railColor}
|
||||
strokeWidth={strokeWidth}
|
||||
percent={status.disk.percent}
|
||||
size={gaugeSize}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ export default function SystemHistoryModal({ open, status, onClose }: SystemHist
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
closable
|
||||
footer={null}
|
||||
width={isMobile ? '95vw' : 900}
|
||||
onCancel={onClose}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ export default function VersionModal({ open, status, onClose, onBusy }: VersionM
|
|||
<Modal
|
||||
open={open}
|
||||
title={t('pages.index.xrayUpdates')}
|
||||
closable
|
||||
footer={null}
|
||||
onCancel={onClose}
|
||||
>
|
||||
|
|
@ -117,7 +116,7 @@ export default function VersionModal({ open, status, onClose, onBusy }: VersionM
|
|||
<Alert
|
||||
type="warning"
|
||||
className="mb-12"
|
||||
message={t('pages.index.xraySwitchClickDesk')}
|
||||
title={t('pages.index.xraySwitchClickDesk')}
|
||||
showIcon
|
||||
/>
|
||||
<List bordered className="version-list">
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
closable
|
||||
footer={null}
|
||||
width={isMobile ? '100vw' : '80vw'}
|
||||
className={isMobile ? 'xraylog-modal-mobile' : undefined}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,6 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
|||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
closable
|
||||
footer={null}
|
||||
width={isMobile ? '95vw' : 900}
|
||||
onCancel={onClose}
|
||||
|
|
@ -249,7 +248,7 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
|||
type="warning"
|
||||
showIcon
|
||||
className="metrics-alert"
|
||||
message={t('pages.index.xrayMetricsDisabled')}
|
||||
title={t('pages.index.xrayMetricsDisabled')}
|
||||
description={state.reason || t('pages.index.xrayMetricsHint')}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -269,7 +268,7 @@ export default function XrayMetricsModal({ open, onClose }: XrayMetricsModalProp
|
|||
type="info"
|
||||
showIcon
|
||||
className="metrics-alert"
|
||||
message={t('pages.index.xrayObservatoryEmpty')}
|
||||
title={t('pages.index.xrayObservatoryEmpty')}
|
||||
description={t('pages.index.xrayObservatoryHint')}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default function XrayStatusCard({
|
|||
const stateText = t(XRAY_STATE_KEYS[status.xray.state] ?? 'pages.index.xrayStatusUnknown');
|
||||
|
||||
const title = (
|
||||
<Space direction="horizontal">
|
||||
<Space>
|
||||
<span>{t('pages.index.xrayStatus')}</span>
|
||||
{isMobile && status.xray.version && status.xray.version !== 'Unknown' && (
|
||||
<Tag color="green">v{status.xray.version}</Tag>
|
||||
|
|
@ -105,21 +105,21 @@ export default function XrayStatusCard({
|
|||
const actions = [
|
||||
...(ipLimitEnable
|
||||
? [
|
||||
<Space direction="horizontal" className="action" key="xraylogs" onClick={onOpenXrayLogs}>
|
||||
<Space className="action" key="xraylogs" onClick={onOpenXrayLogs}>
|
||||
<BarsOutlined />
|
||||
{!isMobile && <span>{t('pages.index.logs')}</span>}
|
||||
</Space>,
|
||||
]
|
||||
: []),
|
||||
<Space direction="horizontal" className="action" key="stop" onClick={onStopXray}>
|
||||
<Space className="action" key="stop" onClick={onStopXray}>
|
||||
<PoweroffOutlined />
|
||||
{!isMobile && <span>{t('pages.index.stopXray')}</span>}
|
||||
</Space>,
|
||||
<Space direction="horizontal" className="action" key="restart" onClick={onRestartXray}>
|
||||
<Space className="action" key="restart" onClick={onRestartXray}>
|
||||
<ReloadOutlined />
|
||||
{!isMobile && <span>{t('pages.index.restartXray')}</span>}
|
||||
</Space>,
|
||||
<Space direction="horizontal" className="action" key="switch" onClick={onOpenVersionSwitch}>
|
||||
<Space className="action" key="switch" onClick={onOpenVersionSwitch}>
|
||||
<ToolOutlined />
|
||||
{!isMobile && (
|
||||
<span>
|
||||
|
|
|
|||
|
|
@ -145,12 +145,12 @@ export default function LoginPage() {
|
|||
{themeIcon}
|
||||
</button>
|
||||
<Popover
|
||||
overlayClassName={isDark ? 'dark' : 'light'}
|
||||
rootClassName={isDark ? 'dark' : 'light'}
|
||||
title={t('pages.settings.language')}
|
||||
placement="bottomRight"
|
||||
trigger="click"
|
||||
content={
|
||||
<Space direction="vertical" size={10} className="settings-popover">
|
||||
<Space orientation="vertical" size={10} className="settings-popover">
|
||||
<Select
|
||||
className="lang-select"
|
||||
value={lang}
|
||||
|
|
@ -240,7 +240,7 @@ export default function LoginPage() {
|
|||
size="large"
|
||||
block
|
||||
>
|
||||
{submitting ? '' : t('login')}
|
||||
{t('login')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ export default function NodeFormModal({
|
|||
confirmLoading={submitting}
|
||||
okText={t('save')}
|
||||
cancelText={t('cancel')}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
width="640px"
|
||||
onOk={onSave}
|
||||
onCancel={close}
|
||||
|
|
@ -276,14 +276,14 @@ export default function NodeFormModal({
|
|||
<Alert
|
||||
type="success"
|
||||
showIcon
|
||||
message={t('pages.nodes.connectionOk', { ms: testResult.latencyMs })}
|
||||
title={t('pages.nodes.connectionOk', { ms: testResult.latencyMs })}
|
||||
description={testResult.xrayVersion ? `Xray ${testResult.xrayVersion}` : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Alert
|
||||
type="error"
|
||||
showIcon
|
||||
message={t('pages.nodes.connectionFailed')}
|
||||
title={t('pages.nodes.connectionFailed')}
|
||||
description={testResult.error}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export default function NodesPage() {
|
|||
|
||||
<Layout className="content-shell">
|
||||
<Layout.Content id="content-layout" className="content-area">
|
||||
<Spin spinning={!fetched} delay={200} tip="Loading…" size="large">
|
||||
<Spin spinning={!fetched} delay={200} description="Loading…" size="large">
|
||||
{!fetched ? (
|
||||
<div className="loading-spacer" />
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ export default function SecurityTab({ allSetting, updateSetting }: SecurityTabPr
|
|||
onChange={(e) => updateUserField('newPassword', e.target.value)} />
|
||||
</SettingListItem>
|
||||
<List.Item>
|
||||
<Space direction="horizontal" style={{ padding: '0 20px' }}>
|
||||
<Space style={{ padding: '0 20px' }}>
|
||||
<Button type="primary" loading={updating} onClick={onUpdateUserClick}>
|
||||
{t('confirm')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { useEffect, useMemo, useState } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Alert,
|
||||
BackTop,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
ConfigProvider,
|
||||
FloatButton,
|
||||
Layout,
|
||||
Modal,
|
||||
Row,
|
||||
|
|
@ -265,7 +265,7 @@ export default function SettingsPage() {
|
|||
|
||||
<Layout className="content-shell">
|
||||
<Layout.Content id="content-layout" className="content-area">
|
||||
<Spin spinning={spinning || !fetched} delay={200} tip="Loading…" size="large">
|
||||
<Spin spinning={spinning || !fetched} delay={200} description="Loading…" size="large">
|
||||
{!fetched ? (
|
||||
<div className="loading-spacer" />
|
||||
) : (
|
||||
|
|
@ -277,7 +277,7 @@ export default function SettingsPage() {
|
|||
closable
|
||||
className="conf-alert"
|
||||
onClose={() => setAlertVisible(false)}
|
||||
message="Security warnings"
|
||||
title="Security warnings"
|
||||
description={(
|
||||
<>
|
||||
<b>Your panel may be exposed:</b>
|
||||
|
|
@ -294,7 +294,7 @@ export default function SettingsPage() {
|
|||
<Card hoverable>
|
||||
<Row className="header-row">
|
||||
<Col xs={24} sm={10} className="header-actions">
|
||||
<Space direction="horizontal">
|
||||
<Space>
|
||||
<Button type="primary" disabled={saveDisabled} onClick={saveAll}>
|
||||
{t('pages.settings.save')}
|
||||
</Button>
|
||||
|
|
@ -304,8 +304,8 @@ export default function SettingsPage() {
|
|||
</Space>
|
||||
</Col>
|
||||
<Col xs={24} sm={14} className="header-info">
|
||||
<BackTop target={scrollTarget} visibilityHeight={200} />
|
||||
<Alert type="warning" showIcon message={t('pages.settings.infoDesc')} />
|
||||
<FloatButton.BackTop target={scrollTarget} visibilityHeight={200} />
|
||||
<Alert type="warning" showIcon title={t('pages.settings.infoDesc')} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ export default function SubscriptionFormatsTab({ allSetting, updateSetting }: Su
|
|||
options={['ip', 'ipv4', 'ipv6'].map((p) => ({ value: p, label: p }))}
|
||||
/>
|
||||
</SettingListItem>
|
||||
<Space direction="horizontal" style={{ padding: '10px 20px' }}>
|
||||
<Space style={{ padding: '10px 20px' }}>
|
||||
{noisesArray.length > 1 && (
|
||||
<Button type="primary" danger onClick={() => removeNoise(index)}>
|
||||
{t('delete')}
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ export default function SubPage() {
|
|||
placement="bottomRight"
|
||||
trigger="click"
|
||||
content={
|
||||
<Space direction="vertical" size={10} className="settings-popover">
|
||||
<Space orientation="vertical" size={10} className="settings-popover">
|
||||
<Select
|
||||
className="lang-select"
|
||||
value={lang}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export default function BalancerFormModal({
|
|||
okText={okText}
|
||||
cancelText={t('close')}
|
||||
okButtonProps={{ disabled: !isValid }}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
onOk={submit}
|
||||
onCancel={onClose}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ export default function BalancersTab({
|
|||
return (
|
||||
<>
|
||||
{modalContextHolder}
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
{rows.length === 0 ? (
|
||||
<Empty description={t('emptyBalancersDesc')}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openAdd}>
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ export default function BasicsTab({
|
|||
<Alert
|
||||
type="warning"
|
||||
className="mb-12 hint-alert"
|
||||
message={t('pages.xray.generalConfigsDesc')}
|
||||
title={t('pages.xray.generalConfigsDesc')}
|
||||
icon={<ExclamationCircleFilled style={{ color: '#FFA031' }} />}
|
||||
/>
|
||||
<SettingListItem
|
||||
|
|
@ -300,7 +300,7 @@ export default function BasicsTab({
|
|||
<Alert
|
||||
type="warning"
|
||||
className="mb-12 hint-alert"
|
||||
message={t('pages.xray.logConfigsDesc')}
|
||||
title={t('pages.xray.logConfigsDesc')}
|
||||
icon={<ExclamationCircleFilled style={{ color: '#FFA031' }} />}
|
||||
/>
|
||||
<SettingListItem
|
||||
|
|
@ -377,7 +377,7 @@ export default function BasicsTab({
|
|||
<Alert
|
||||
type="warning"
|
||||
className="mb-12 hint-alert"
|
||||
message={t('pages.xray.blockConnectionsConfigsDesc')}
|
||||
title={t('pages.xray.blockConnectionsConfigsDesc')}
|
||||
icon={<ExclamationCircleFilled style={{ color: '#FFA031' }} />}
|
||||
/>
|
||||
|
||||
|
|
@ -428,7 +428,7 @@ export default function BasicsTab({
|
|||
<Alert
|
||||
type="warning"
|
||||
className="mb-12 hint-alert"
|
||||
message={t('pages.xray.directConnectionsConfigsDesc')}
|
||||
title={t('pages.xray.directConnectionsConfigsDesc')}
|
||||
icon={<ExclamationCircleFilled style={{ color: '#FFA031' }} />}
|
||||
/>
|
||||
|
||||
|
|
@ -532,7 +532,7 @@ export default function BasicsTab({
|
|||
key: 'reset',
|
||||
label: t('pages.settings.resetDefaultConfig'),
|
||||
children: (
|
||||
<Space direction="horizontal" style={{ padding: '0 20px' }}>
|
||||
<Space style={{ padding: '0 20px' }}>
|
||||
<Button danger onClick={confirmResetDefault}>
|
||||
{t('pages.settings.resetDefaultConfig')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default function DnsPresetsModal({ open, onClose, onInstall }: DnsPresets
|
|||
open={open}
|
||||
title={t('pages.xray.dns.dnsPresetTitle')}
|
||||
footer={null}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
onCancel={onClose}
|
||||
>
|
||||
<List bordered>
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ export default function DnsServerModal({
|
|||
title={title}
|
||||
okText={t('confirm')}
|
||||
cancelText={t('close')}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
onOk={submit}
|
||||
onCancel={onClose}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab
|
|||
</Button>
|
||||
</Empty>
|
||||
) : (
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={() => syncHosts([...hostsList, { domain: '', values: [] }])}>
|
||||
{t('pages.xray.dns.hostsAdd')}
|
||||
</Button>
|
||||
|
|
@ -475,7 +475,7 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab
|
|||
</Space>
|
||||
</Empty>
|
||||
) : (
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space wrap>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openAddServer}>
|
||||
{t('pages.xray.dns.add')}
|
||||
|
|
@ -509,7 +509,7 @@ export default function DnsTab({ templateSettings, setTemplateSettings }: DnsTab
|
|||
</Button>
|
||||
</Empty>
|
||||
) : (
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={addFakedns}>
|
||||
{t('pages.xray.fakedns.add')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ export default function NordModal({
|
|||
}
|
||||
|
||||
return (
|
||||
<Modal open={open} title="NordVPN NordLynx" footer={null} closable maskClosable onCancel={onClose}>
|
||||
<Modal open={open} title="NordVPN NordLynx" footer={null} onCancel={onClose}>
|
||||
{nordData == null ? (
|
||||
<Tabs
|
||||
defaultActiveKey="token"
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ export default function OutboundFormModal({
|
|||
title={title}
|
||||
okText={okText}
|
||||
cancelText={t('close')}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
width={780}
|
||||
onOk={onOk}
|
||||
onCancel={onClose}
|
||||
|
|
@ -434,7 +434,7 @@ export default function OutboundFormModal({
|
|||
key: '2',
|
||||
label: 'JSON',
|
||||
children: (
|
||||
<Space direction="vertical" size={10} style={{ width: '100%', marginTop: 10 }}>
|
||||
<Space orientation="vertical" size={10} style={{ width: '100%', marginTop: 10 }}>
|
||||
<Input.Search
|
||||
value={linkInput}
|
||||
placeholder="vmess:// vless:// trojan:// ss:// hysteria2://"
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ export default function OutboundsTab({
|
|||
return (
|
||||
<Popover
|
||||
placement="topLeft"
|
||||
overlayClassName="outbound-test-popover"
|
||||
rootClassName="outbound-test-popover"
|
||||
content={
|
||||
<div className="timing-breakdown">
|
||||
<div className={`td-head ${r.success ? 'ok' : 'fail'}`}>
|
||||
|
|
@ -386,7 +386,7 @@ export default function OutboundsTab({
|
|||
return (
|
||||
<>
|
||||
{modalContextHolder}
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Row gutter={[12, 12]} align="middle" justify="space-between">
|
||||
<Col xs={24} sm={12}>
|
||||
<Space size="small" wrap>
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ export default function RoutingTab({
|
|||
return (
|
||||
<>
|
||||
{modalContextHolder}
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openAdd}>
|
||||
{t('pages.xray.Routings')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ export default function RuleFormModal({
|
|||
title={title}
|
||||
okText={okText}
|
||||
cancelText={t('close')}
|
||||
maskClosable={false}
|
||||
mask={{ closable: false }}
|
||||
width={640}
|
||||
onOk={submit}
|
||||
onCancel={onClose}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ export default function WarpModal({
|
|||
const hasConfig = !ObjectUtil.isEmpty(warpConfig);
|
||||
|
||||
return (
|
||||
<Modal open={open} title="Cloudflare WARP" footer={null} closable maskClosable onCancel={onClose}>
|
||||
<Modal open={open} title="Cloudflare WARP" footer={null} onCancel={onClose}>
|
||||
{!hasWarp ? (
|
||||
<Button type="primary" loading={loading} icon={<ApiOutlined />} onClick={register}>
|
||||
Create WARP account
|
||||
|
|
@ -268,7 +268,7 @@ export default function WarpModal({
|
|||
Update
|
||||
</Button>
|
||||
{licenseError && (
|
||||
<Alert message={licenseError} type="error" showIcon className="license-error" />
|
||||
<Alert title={licenseError} type="error" showIcon className="license-error" />
|
||||
)}
|
||||
</div>
|
||||
</Form.Item>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
BackTop,
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
ConfigProvider,
|
||||
FloatButton,
|
||||
Layout,
|
||||
message,
|
||||
Modal,
|
||||
|
|
@ -258,7 +258,7 @@ export default function XrayPage() {
|
|||
|
||||
<Layout className="content-shell">
|
||||
<Layout.Content id="content-layout" className="content-area">
|
||||
<Spin spinning={spinning || !fetched} delay={200} tip="Loading…" size="large">
|
||||
<Spin spinning={spinning || !fetched} delay={200} description="Loading…" size="large">
|
||||
{!fetched ? (
|
||||
<div className="loading-spacer" />
|
||||
) : fetchError ? (
|
||||
|
|
@ -274,7 +274,7 @@ export default function XrayPage() {
|
|||
<Card hoverable>
|
||||
<Row className="header-row">
|
||||
<Col xs={24} sm={14} className="header-actions">
|
||||
<Space direction="horizontal">
|
||||
<Space>
|
||||
<Button type="primary" disabled={saveDisabled} onClick={onSaveAll}>
|
||||
{t('pages.xray.save')}
|
||||
</Button>
|
||||
|
|
@ -293,8 +293,8 @@ export default function XrayPage() {
|
|||
</Space>
|
||||
</Col>
|
||||
<Col xs={24} sm={10} className="header-info">
|
||||
<BackTop target={scrollTarget} visibilityHeight={200} />
|
||||
<Alert type="warning" showIcon message={t('pages.settings.infoDesc')} />
|
||||
<FloatButton.BackTop target={scrollTarget} visibilityHeight={200} />
|
||||
<Alert type="warning" showIcon title={t('pages.settings.infoDesc')} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
|
|
|||
Loading…
Reference in a new issue