mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-31 15:37:14 +00:00
feat(ui): use CodeMirror editor for Import Inbound and Inbound JSON
This commit is contained in:
@@ -3,6 +3,8 @@ import { Input, Modal } from 'antd';
|
|||||||
import type { InputRef } from 'antd';
|
import type { InputRef } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import JsonEditor from '@/components/form/JsonEditor';
|
||||||
|
|
||||||
interface PromptModalProps {
|
interface PromptModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -11,6 +13,7 @@ interface PromptModalProps {
|
|||||||
type?: 'input' | 'textarea';
|
type?: 'input' | 'textarea';
|
||||||
initialValue?: string;
|
initialValue?: string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
json?: boolean;
|
||||||
onConfirm: (value: string) => void;
|
onConfirm: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ export default function PromptModal({
|
|||||||
type = 'input',
|
type = 'input',
|
||||||
initialValue = '',
|
initialValue = '',
|
||||||
loading = false,
|
loading = false,
|
||||||
|
json = false,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
}: PromptModalProps) {
|
}: PromptModalProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -63,7 +67,9 @@ export default function PromptModal({
|
|||||||
onCancel={onClose}
|
onCancel={onClose}
|
||||||
destroyOnHidden
|
destroyOnHidden
|
||||||
>
|
>
|
||||||
{type === 'textarea' ? (
|
{json ? (
|
||||||
|
<JsonEditor value={value} onChange={setValue} minHeight="240px" maxHeight="60vh" />
|
||||||
|
) : type === 'textarea' ? (
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
ref={(el) => { textareaRef.current = (el as unknown as { resizableTextArea?: { textArea: HTMLTextAreaElement } })?.resizableTextArea?.textArea ?? null; }}
|
ref={(el) => { textareaRef.current = (el as unknown as { resizableTextArea?: { textArea: HTMLTextAreaElement } })?.resizableTextArea?.textArea ?? null; }}
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Button, Input, Modal, message } from 'antd';
|
|||||||
import { CopyOutlined, DownloadOutlined } from '@ant-design/icons';
|
import { CopyOutlined, DownloadOutlined } from '@ant-design/icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import JsonEditor from '@/components/form/JsonEditor';
|
||||||
import { ClipboardManager, FileManager } from '@/utils';
|
import { ClipboardManager, FileManager } from '@/utils';
|
||||||
|
|
||||||
interface TextModalProps {
|
interface TextModalProps {
|
||||||
@@ -10,9 +11,10 @@ interface TextModalProps {
|
|||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
fileName?: string;
|
fileName?: string;
|
||||||
|
json?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TextModal({ open, onClose, title, content, fileName = '' }: TextModalProps) {
|
export default function TextModal({ open, onClose, title, content, fileName = '', json = false }: TextModalProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [messageApi, messageContextHolder] = message.useMessage();
|
const [messageApi, messageContextHolder] = message.useMessage();
|
||||||
async function copy() {
|
async function copy() {
|
||||||
@@ -45,16 +47,20 @@ export default function TextModal({ open, onClose, title, content, fileName = ''
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Input.TextArea
|
{json ? (
|
||||||
value={content}
|
<JsonEditor value={content} readOnly minHeight="240px" maxHeight="60vh" />
|
||||||
readOnly
|
) : (
|
||||||
autoSize={{ minRows: 10, maxRows: 20 }}
|
<Input.TextArea
|
||||||
style={{
|
value={content}
|
||||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
readOnly
|
||||||
fontSize: 12,
|
autoSize={{ minRows: 10, maxRows: 20 }}
|
||||||
overflowY: 'auto',
|
style={{
|
||||||
}}
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
||||||
/>
|
fontSize: 12,
|
||||||
|
overflowY: 'auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -146,12 +146,14 @@ export default function InboundsPage() {
|
|||||||
const [textTitle, setTextTitle] = useState('');
|
const [textTitle, setTextTitle] = useState('');
|
||||||
const [textContent, setTextContent] = useState('');
|
const [textContent, setTextContent] = useState('');
|
||||||
const [textFileName, setTextFileName] = useState('');
|
const [textFileName, setTextFileName] = useState('');
|
||||||
|
const [textJson, setTextJson] = useState(false);
|
||||||
|
|
||||||
const [promptOpen, setPromptOpen] = useState(false);
|
const [promptOpen, setPromptOpen] = useState(false);
|
||||||
const [promptTitle, setPromptTitle] = useState('');
|
const [promptTitle, setPromptTitle] = useState('');
|
||||||
const [promptOkText, setPromptOkText] = useState('OK');
|
const [promptOkText, setPromptOkText] = useState('OK');
|
||||||
const [promptType, setPromptType] = useState<'textarea' | 'input'>('textarea');
|
const [promptType, setPromptType] = useState<'textarea' | 'input'>('textarea');
|
||||||
const [promptInitial, setPromptInitial] = useState('');
|
const [promptInitial, setPromptInitial] = useState('');
|
||||||
|
const [promptJson, setPromptJson] = useState(false);
|
||||||
const [promptLoading, setPromptLoading] = useState(false);
|
const [promptLoading, setPromptLoading] = useState(false);
|
||||||
const [promptHandler, setPromptHandler] = useState<((value: string) => Promise<boolean | void> | boolean | void) | null>(null);
|
const [promptHandler, setPromptHandler] = useState<((value: string) => Promise<boolean | void> | boolean | void) | null>(null);
|
||||||
|
|
||||||
@@ -163,10 +165,11 @@ export default function InboundsPage() {
|
|||||||
const infoNodeAddress = useMemo(() => hostOverrideFor(infoDbInbound), [infoDbInbound, hostOverrideFor]);
|
const infoNodeAddress = useMemo(() => hostOverrideFor(infoDbInbound), [infoDbInbound, hostOverrideFor]);
|
||||||
const qrNodeAddress = useMemo(() => hostOverrideFor(qrDbInbound), [qrDbInbound, hostOverrideFor]);
|
const qrNodeAddress = useMemo(() => hostOverrideFor(qrDbInbound), [qrDbInbound, hostOverrideFor]);
|
||||||
|
|
||||||
const openText = useCallback((opts: { title: string; content: string; fileName?: string }) => {
|
const openText = useCallback((opts: { title: string; content: string; fileName?: string; json?: boolean }) => {
|
||||||
setTextTitle(opts.title);
|
setTextTitle(opts.title);
|
||||||
setTextContent(opts.content);
|
setTextContent(opts.content);
|
||||||
setTextFileName(opts.fileName || '');
|
setTextFileName(opts.fileName || '');
|
||||||
|
setTextJson(opts.json || false);
|
||||||
setTextOpen(true);
|
setTextOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -175,12 +178,14 @@ export default function InboundsPage() {
|
|||||||
okText?: string;
|
okText?: string;
|
||||||
type?: 'textarea' | 'input';
|
type?: 'textarea' | 'input';
|
||||||
value?: string;
|
value?: string;
|
||||||
|
json?: boolean;
|
||||||
confirm: (value: string) => Promise<boolean | void> | boolean | void;
|
confirm: (value: string) => Promise<boolean | void> | boolean | void;
|
||||||
}) => {
|
}) => {
|
||||||
setPromptTitle(opts.title);
|
setPromptTitle(opts.title);
|
||||||
setPromptOkText(opts.okText || t('confirm'));
|
setPromptOkText(opts.okText || t('confirm'));
|
||||||
setPromptType(opts.type || 'textarea');
|
setPromptType(opts.type || 'textarea');
|
||||||
setPromptInitial(opts.value || '');
|
setPromptInitial(opts.value || '');
|
||||||
|
setPromptJson(opts.json || false);
|
||||||
setPromptHandler(() => opts.confirm);
|
setPromptHandler(() => opts.confirm);
|
||||||
setPromptOpen(true);
|
setPromptOpen(true);
|
||||||
}, [t]);
|
}, [t]);
|
||||||
@@ -267,7 +272,7 @@ export default function InboundsPage() {
|
|||||||
}, [checkFallback, remarkModel, hostOverrideFor, subSettings.publicHost, openText, t]);
|
}, [checkFallback, remarkModel, hostOverrideFor, subSettings.publicHost, openText, t]);
|
||||||
|
|
||||||
const exportInboundClipboard = useCallback((dbInbound: DBInbound) => {
|
const exportInboundClipboard = useCallback((dbInbound: DBInbound) => {
|
||||||
openText({ title: t('pages.inbounds.inboundJsonTitle'), content: JSON.stringify(dbInbound, null, 2) });
|
openText({ title: t('pages.inbounds.inboundJsonTitle'), content: JSON.stringify(dbInbound, null, 2), json: true });
|
||||||
}, [openText, t]);
|
}, [openText, t]);
|
||||||
|
|
||||||
const exportInboundSubs = useCallback((dbInbound: DBInbound) => {
|
const exportInboundSubs = useCallback((dbInbound: DBInbound) => {
|
||||||
@@ -327,6 +332,7 @@ export default function InboundsPage() {
|
|||||||
okText: t('pages.inbounds.import'),
|
okText: t('pages.inbounds.import'),
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
value: '',
|
value: '',
|
||||||
|
json: true,
|
||||||
confirm: async (value) => {
|
confirm: async (value) => {
|
||||||
const msg = await HttpUtil.post('/panel/api/inbounds/import', { data: value });
|
const msg = await HttpUtil.post('/panel/api/inbounds/import', { data: value });
|
||||||
if (msg?.success) {
|
if (msg?.success) {
|
||||||
@@ -705,6 +711,7 @@ export default function InboundsPage() {
|
|||||||
title={textTitle}
|
title={textTitle}
|
||||||
content={textContent}
|
content={textContent}
|
||||||
fileName={textFileName}
|
fileName={textFileName}
|
||||||
|
json={textJson}
|
||||||
/>
|
/>
|
||||||
</LazyMount>
|
</LazyMount>
|
||||||
<LazyMount when={promptOpen}>
|
<LazyMount when={promptOpen}>
|
||||||
@@ -716,6 +723,7 @@ export default function InboundsPage() {
|
|||||||
type={promptType}
|
type={promptType}
|
||||||
initialValue={promptInitial}
|
initialValue={promptInitial}
|
||||||
loading={promptLoading}
|
loading={promptLoading}
|
||||||
|
json={promptJson}
|
||||||
onConfirm={onPromptConfirm}
|
onConfirm={onPromptConfirm}
|
||||||
/>
|
/>
|
||||||
</LazyMount>
|
</LazyMount>
|
||||||
|
|||||||
Reference in New Issue
Block a user