Parcourir la source

feat(ui): use CodeMirror editor for Import Inbound and Inbound JSON

MHSanaei il y a 1 jour
Parent
commit
0cefadd166

+ 7 - 1
frontend/src/components/feedback/PromptModal.tsx

@@ -3,6 +3,8 @@ import { Input, Modal } from 'antd';
 import type { InputRef } from 'antd';
 import { useTranslation } from 'react-i18next';
 
+import JsonEditor from '@/components/form/JsonEditor';
+
 interface PromptModalProps {
   open: boolean;
   onClose: () => void;
@@ -11,6 +13,7 @@ interface PromptModalProps {
   type?: 'input' | 'textarea';
   initialValue?: string;
   loading?: boolean;
+  json?: boolean;
   onConfirm: (value: string) => void;
 }
 
@@ -22,6 +25,7 @@ export default function PromptModal({
   type = 'input',
   initialValue = '',
   loading = false,
+  json = false,
   onConfirm,
 }: PromptModalProps) {
   const { t } = useTranslation();
@@ -63,7 +67,9 @@ export default function PromptModal({
       onCancel={onClose}
       destroyOnHidden
     >
-      {type === 'textarea' ? (
+      {json ? (
+        <JsonEditor value={value} onChange={setValue} minHeight="240px" maxHeight="60vh" />
+      ) : type === 'textarea' ? (
         <Input.TextArea
           ref={(el) => { textareaRef.current = (el as unknown as { resizableTextArea?: { textArea: HTMLTextAreaElement } })?.resizableTextArea?.textArea ?? null; }}
           value={value}

+ 17 - 11
frontend/src/components/feedback/TextModal.tsx

@@ -2,6 +2,7 @@ import { Button, Input, Modal, message } from 'antd';
 import { CopyOutlined, DownloadOutlined } from '@ant-design/icons';
 import { useTranslation } from 'react-i18next';
 
+import JsonEditor from '@/components/form/JsonEditor';
 import { ClipboardManager, FileManager } from '@/utils';
 
 interface TextModalProps {
@@ -10,9 +11,10 @@ interface TextModalProps {
   title: string;
   content: 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 [messageApi, messageContextHolder] = message.useMessage();
   async function copy() {
@@ -45,16 +47,20 @@ export default function TextModal({ open, onClose, title, content, fileName = ''
         </>
       )}
     >
-      <Input.TextArea
-        value={content}
-        readOnly
-        autoSize={{ minRows: 10, maxRows: 20 }}
-        style={{
-          fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
-          fontSize: 12,
-          overflowY: 'auto',
-        }}
-      />
+      {json ? (
+        <JsonEditor value={content} readOnly minHeight="240px" maxHeight="60vh" />
+      ) : (
+        <Input.TextArea
+          value={content}
+          readOnly
+          autoSize={{ minRows: 10, maxRows: 20 }}
+          style={{
+            fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
+            fontSize: 12,
+            overflowY: 'auto',
+          }}
+        />
+      )}
       </Modal>
     </>
   );

+ 10 - 2
frontend/src/pages/inbounds/InboundsPage.tsx

@@ -146,12 +146,14 @@ export default function InboundsPage() {
   const [textTitle, setTextTitle] = useState('');
   const [textContent, setTextContent] = useState('');
   const [textFileName, setTextFileName] = useState('');
+  const [textJson, setTextJson] = useState(false);
 
   const [promptOpen, setPromptOpen] = useState(false);
   const [promptTitle, setPromptTitle] = useState('');
   const [promptOkText, setPromptOkText] = useState('OK');
   const [promptType, setPromptType] = useState<'textarea' | 'input'>('textarea');
   const [promptInitial, setPromptInitial] = useState('');
+  const [promptJson, setPromptJson] = useState(false);
   const [promptLoading, setPromptLoading] = useState(false);
   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 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);
     setTextContent(opts.content);
     setTextFileName(opts.fileName || '');
+    setTextJson(opts.json || false);
     setTextOpen(true);
   }, []);
 
@@ -175,12 +178,14 @@ export default function InboundsPage() {
     okText?: string;
     type?: 'textarea' | 'input';
     value?: string;
+    json?: boolean;
     confirm: (value: string) => Promise<boolean | void> | boolean | void;
   }) => {
     setPromptTitle(opts.title);
     setPromptOkText(opts.okText || t('confirm'));
     setPromptType(opts.type || 'textarea');
     setPromptInitial(opts.value || '');
+    setPromptJson(opts.json || false);
     setPromptHandler(() => opts.confirm);
     setPromptOpen(true);
   }, [t]);
@@ -267,7 +272,7 @@ export default function InboundsPage() {
   }, [checkFallback, remarkModel, hostOverrideFor, subSettings.publicHost, openText, t]);
 
   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]);
 
   const exportInboundSubs = useCallback((dbInbound: DBInbound) => {
@@ -327,6 +332,7 @@ export default function InboundsPage() {
       okText: t('pages.inbounds.import'),
       type: 'textarea',
       value: '',
+      json: true,
       confirm: async (value) => {
         const msg = await HttpUtil.post('/panel/api/inbounds/import', { data: value });
         if (msg?.success) {
@@ -705,6 +711,7 @@ export default function InboundsPage() {
             title={textTitle}
             content={textContent}
             fileName={textFileName}
+            json={textJson}
           />
         </LazyMount>
         <LazyMount when={promptOpen}>
@@ -716,6 +723,7 @@ export default function InboundsPage() {
             type={promptType}
             initialValue={promptInitial}
             loading={promptLoading}
+            json={promptJson}
             onConfirm={onPromptConfirm}
           />
         </LazyMount>