You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2021/06/15 01:33:13 UTC

[apisix-dashboard] branch master updated: feat: Use monaco editor (#1907)

This is an automated email from the ASF dual-hosted git repository.

juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 421261f  feat: Use monaco editor (#1907)
421261f is described below

commit 421261fbf897c5cf4d670bf5c3fbd4730c3336bd
Author: qian0817 <94...@qq.com>
AuthorDate: Tue Jun 15 09:33:01 2021 +0800

    feat: Use monaco editor (#1907)
---
 .gitignore                                         |   3 +-
 web/cypress/fixtures/selector.json                 |   6 +-
 ...e-consumer-with-api-breaker-plugin-form.spec.js |  20 +-
 ...e-consumer-with-limit-count-plugin-form.spec.js |  20 +-
 .../create-consumer-with-proxy-mirror-form.spec.js |  24 +--
 .../create-upstream-with-cors-form.spec.js         |  23 +--
 .../create-upstream-with-limit-req-form.spec.js    |  22 +-
 .../consumer/create-with-limit-conn-form.spec.js   |  22 +-
 .../create-with-referer-restriction-form.spec.js   |  20 +-
 .../consumer/create_and_delete_consumer.spec.js    |  44 ++--
 .../plugin/create-delete-in-drawer-plugin.spec.js  |   6 +-
 .../plugin/create-edit-delete-plugin.spec.js       |   9 +-
 .../create-route-with-plugin-orchestration.spec.js |   4 +-
 .../create-edit-delete-plugin-template.spec.js     |   4 +-
 .../rawDataEditor/test-rawDataEditor.spec.js       |  23 ++-
 .../create-edit-duplicate-delete-route.spec.js     |  12 +-
 .../service/create-edit-delete-service.spec.js     |  10 +-
 .../upstream/create_and_delete_upstream.spec.js    |   6 +-
 web/cypress/support/commands.js                    |  26 +--
 web/package.json                                   |  21 +-
 web/src/components/Plugin/PluginDetail.tsx         | 225 +++++++++++----------
 web/src/components/Plugin/PluginPage.tsx           |   4 +-
 web/src/components/Plugin/typing.d.ts              |   8 +-
 web/src/components/PluginFlow/PluginFlow.tsx       |   4 +-
 web/src/components/RawDataEditor/RawDataEditor.tsx | 178 ++++++++--------
 web/src/global.tsx                                 |   9 +
 web/src/locales/en-US/component.ts                 |   3 +
 web/src/locales/zh-CN/component.ts                 |   3 +
 web/src/pages/Plugin/List.tsx                      |   4 +-
 .../Route/components/DebugViews/DebugDrawView.tsx  | 135 ++++++++-----
 web/src/pages/Route/constants.ts                   |  13 +-
 web/tsconfig.json                                  |   4 +
 web/yarn.lock                                      |  46 +++--
 33 files changed, 500 insertions(+), 461 deletions(-)

diff --git a/.gitignore b/.gitignore
index 10e2061..be85770 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,8 +60,7 @@ web/coverage
 web/cypress/screenshots/
 web/cypress/videos/
 web/cypress/downloads/
-web/cypress/screenshots/
-web/cypress/videos/
+web/public/monaco-editor/
 
 # vim
 *.swp
diff --git a/web/cypress/fixtures/selector.json b/web/cypress/fixtures/selector.json
index d6691b9..83b1924 100644
--- a/web/cypress/fixtures/selector.json
+++ b/web/cypress/fixtures/selector.json
@@ -1,5 +1,4 @@
 {
-  "codeMirror": ".CodeMirror",
   "username": "#username",
   "languageSwitcher": ".ant-space-align-center",
   "dropdown": ".rc-virtual-list",
@@ -15,7 +14,6 @@
   "tableCell": ".ant-table-cell",
   "empty": ".ant-empty-normal",
   "refresh": ".anticon-reload",
-  "codemirror": ".CodeMirror",
   "disabledSwitcher": "#disable",
   "checkedSwitcher": ".ant-switch-checked",
   "deleteButton": ".ant-btn-dangerous",
@@ -60,7 +58,7 @@
   "passwordInput": "#control-ref_password",
   "drawer": ".ant-drawer-content",
   "drawerFooter": ".ant-drawer-footer",
-  "codemirrorScroll": ".CodeMirror-scroll",
+  "monacoScroll": ".monaco-scrollable-element",
   "drawerClose": ".ant-drawer-close",
   "descriptionSelector": "[title=Description]",
   "customSelector": "[title=Custom]",
@@ -74,7 +72,7 @@
   "pageTwo": ".ant-pagination-item-2",
   "pageTwoActived": ".ant-pagination-item-2.ant-pagination-item-active",
   "selectDropdown": ".ant-select-dropdown",
-  "codeMirrorMode": "[data-cy='code-mirror-mode']",
+  "monacoMode": "[data-cy='monaco-mode']",
   "selectJSON": ".ant-select-dropdown [label=JSON]",
   "deleteAlert": ".ant-modal-body"
 }
diff --git a/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js
index 7891a11..a4df5d2 100644
--- a/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js
+++ b/web/cypress/integration/consumer/create-consumer-with-api-breaker-plugin-form.spec.js
@@ -26,8 +26,8 @@ context('Create and delete consumer with api-breaker plugin form', () => {
     pluginCard: '.ant-card',
     drawer: '.ant-drawer-content',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -59,18 +59,14 @@ context('Create and delete consumer with api-breaker plugin form', () => {
       });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
     cy.get(selector.disabledSwitcher).click();
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'api-breaker').within(() => {
       cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js
index 6e45667..249d4a5 100644
--- a/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js
+++ b/web/cypress/integration/consumer/create-consumer-with-limit-count-plugin-form.spec.js
@@ -26,7 +26,6 @@ context('Create and delete consumer with limit-count plugin form', () => {
     drawer: '.ant-drawer-content',
     dropdown: '.rc-virtual-list',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     count: '#count',
     time_window: '#time_window',
@@ -40,6 +39,7 @@ context('Create and delete consumer with limit-count plugin form', () => {
     redis_cluster_name: '#redis_cluster_name',
     redis_cluster_nodes_0: '#redis_cluster_nodes_0',
     redis_cluster_nodes_1: '#redis_cluster_nodes_1',
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -70,18 +70,14 @@ context('Create and delete consumer with limit-count plugin form', () => {
       });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
     cy.get(selector.disabledSwitcher).click();
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'limit-count').within(() => {
       cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js b/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js
index e6f7f6b..f49f35c 100644
--- a/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js
+++ b/web/cypress/integration/consumer/create-consumer-with-proxy-mirror-form.spec.js
@@ -26,13 +26,13 @@ context('Create and delete consumer with proxy-mirror plugin form', () => {
     drawer: '.ant-drawer-content',
     dropdown: '.rc-virtual-list',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     notificationCloseIcon: '.ant-notification-close-icon',
     max_age: '#max_age',
     allow_origins_by_regex: '#allow_origins_by_regex_0',
     host: '#host',
-    alert: '[role=alert]'
+    alert: '[role=alert]',
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -64,18 +64,14 @@ context('Create and delete consumer with proxy-mirror plugin form', () => {
       });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
     cy.get(selector.disabledSwitcher).click();
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'proxy-mirror').within(() => {
       cy.contains('Enable').click({
@@ -98,7 +94,7 @@ context('Create and delete consumer with proxy-mirror plugin form', () => {
 
     // config proxy-mirror form with correct host
     cy.get(selector.host).clear().type('http://127.0.0.1:1999');
-    cy.get(selector.alert).should('not.exist');
+    cy.get(selector.alert).should('not.be.visible');
     cy.get(selector.disabledSwitcher).click();
     cy.get(selector.drawer).within(() => {
       cy.contains('Submit').click({
diff --git a/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js b/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js
index f6450a0..03cb35c 100644
--- a/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js
+++ b/web/cypress/integration/consumer/create-upstream-with-cors-form.spec.js
@@ -26,7 +26,6 @@ context('Create and Delete Consumer', () => {
     drawer: '.ant-drawer-content',
     dropdown: '.rc-virtual-list',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     notificationCloseIcon: '.ant-notification-close-icon',
     rate: '#rate',
@@ -34,7 +33,8 @@ context('Create and Delete Consumer', () => {
     key: '#key',
     remote_addr: '[title=remote_addr]',
     max_age: '#max_age',
-    allow_origins_by_regex: '#allow_origins_by_regex_0'
+    allow_origins_by_regex: '#allow_origins_by_regex_0',
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -66,18 +66,15 @@ context('Create and Delete Consumer', () => {
       });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
     cy.get(selector.disabledSwitcher).click().should('have.class', 'ant-switch-checked');
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'cors').within(() => {
       cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js b/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js
index 1e375ef..1c4fa7f 100644
--- a/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js
+++ b/web/cypress/integration/consumer/create-upstream-with-limit-req-form.spec.js
@@ -26,12 +26,12 @@ context('Create and Delete Consumer', () => {
     drawer: '.ant-drawer-content',
     dropdown: '.rc-virtual-list',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     rate: '#rate',
     burst: '#burst',
     key: '#key',
-    remote_addr: '[title=remote_addr]'
+    remote_addr: '[title=remote_addr]',
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -61,18 +61,14 @@ context('Create and Delete Consumer', () => {
       cy.contains('Enable').click({ force: true });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
+
+    // edit monaco
     cy.get(selector.disabledSwitcher).click().should('have.class', 'ant-switch-checked');
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'limit-req').within(() => {
       cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js b/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
index 2dbfc4c..4d096f3 100644
--- a/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
+++ b/web/cypress/integration/consumer/create-with-limit-conn-form.spec.js
@@ -26,7 +26,6 @@ context('Create and delete consumer with limit-conn plugin form', () => {
     drawer: '.ant-drawer-content',
     dropdown: '.rc-virtual-list',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     selectDropdown: '.ant-select-dropdown',
     conn: '#conn',
@@ -34,7 +33,8 @@ context('Create and delete consumer with limit-conn plugin form', () => {
     default_conn_delay: '#default_conn_delay',
     key: '#key',
     rejected_code: '#rejected_code',
-    title: '[title="remote_addr"]'
+    title: '[title="remote_addr"]',
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -69,18 +69,14 @@ context('Create and delete consumer with limit-conn plugin form', () => {
       });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
     cy.get(selector.disabledSwitcher).click();
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'limit-conn').within(() => {
       cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js b/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js
index 304b928..eb9d7c2 100644
--- a/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js
+++ b/web/cypress/integration/consumer/create-with-referer-restriction-form.spec.js
@@ -26,10 +26,10 @@ context('Create and delete Consumer with referer-restriction form ', () => {
     drawer: '.ant-drawer-content',
     dropdown: '.rc-virtual-list',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     whitelist: "#whitelist_0",
     bypass_missing: "#bypass_missing",
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -61,18 +61,14 @@ context('Create and delete Consumer with referer-restriction form ', () => {
       });
     });
     cy.focused(selector.drawer).should('exist');
+    cy.get(selector.monacoViewZones).should('exist');
     cy.get(selector.disabledSwitcher).click();
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
 
     cy.contains(selector.pluginCard, 'referer-restriction').within(() => {
       cy.contains('Enable').click({
diff --git a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js
index 1e00b66..ffd669d 100644
--- a/web/cypress/integration/consumer/create_and_delete_consumer.spec.js
+++ b/web/cypress/integration/consumer/create_and_delete_consumer.spec.js
@@ -25,11 +25,11 @@ context('Create and Delete Consumer', () => {
     pluginCard: '.ant-card',
     drawer: '.ant-drawer-content',
     disabledSwitcher: '#disable',
-    codeMirror: '.CodeMirror',
     notification: '.ant-notification-notice-message',
     nameSelector: '[title=Name]',
     serviceSelector: '[title=test_service]',
-    codemirrorScroll: '.CodeMirror-scroll',
+    monacoScroll: ".monaco-scrollable-element",
+    monacoViewZones: '.view-zones'
   }
 
   const data = {
@@ -37,7 +37,7 @@ context('Create and Delete Consumer', () => {
     description: 'desc_by_autotest',
     createConsumerSuccess: 'Create Consumer Successfully',
     deleteConsumerSuccess: 'Delete Consumer Successfully',
-    pluginErrorAlert: 'Invalid plugin data',
+    pluginErrorAlert: 'Invalid plugin data'
   }
 
   beforeEach(() => {
@@ -65,17 +65,13 @@ context('Create and Delete Consumer', () => {
     });
     cy.focused(selector.drawer).should('exist');
     cy.get(selector.disabledSwitcher).click();
-    // edit codemirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
     cy.contains('button', 'Next').click();
     cy.contains('button', 'Submit').click();
     cy.get(selector.notification).should('contain', data.createConsumerSuccess);
@@ -89,7 +85,7 @@ context('Create and Delete Consumer', () => {
     cy.contains(data.consumerName).siblings().contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('plugins').should('exist');
       cy.contains(data.consumerName).should('exist');
     });
@@ -116,17 +112,13 @@ context('Create and Delete Consumer', () => {
         force: true,
       });
     });
-    // edit codeMirror
-    cy.get(selector.codeMirror)
-      .first()
-      .then((editor) => {
-        editor[0].CodeMirror.setValue(
-          JSON.stringify({
-            key_not_exst: 'test',
-          }),
-        );
-        cy.contains('button', 'Submit').click();
-      });
+
+    // edit monaco
+    cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+    cy.window().then((window) => {
+      window.monacoEditor.setValue(JSON.stringify({ key_not_exist: 'test' }));
+      cy.contains('button', 'Submit').click();
+    });
     cy.get(selector.notification).should('contain', data.pluginErrorAlert);
   });
 });
diff --git a/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js b/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js
index bcde1e3..ca633af 100644
--- a/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js
+++ b/web/cypress/integration/plugin/create-delete-in-drawer-plugin.spec.js
@@ -23,7 +23,7 @@ context('Delete Plugin List with the Drawer', () => {
     pluginCardBordered: '.ant-card-bordered',
     drawer: '.ant-drawer-content',
     selectDropdown: '.ant-select-dropdown',
-    codeMirrorMode: "[data-cy='code-mirror-mode']",
+    monacoMode: "[data-cy='monaco-mode']",
     selectJSON: '.ant-select-dropdown [label=JSON]',
     drawerFooter: '.ant-drawer-footer',
     disabledSwitcher: '#disable',
@@ -51,9 +51,9 @@ context('Delete Plugin List with the Drawer', () => {
       });
     });
 
-    cy.get(selector.codeMirrorMode).invoke('text').then(text => {
+    cy.get(selector.monacoMode).invoke('text').then(text => {
       if (text === 'Form') {
-        cy.get(selector.codeMirrorMode).click();
+        cy.get(selector.monacoMode).click();
         cy.get(selector.selectDropdown).should('be.visible');
         cy.get(selector.selectJSON).click();
       }
diff --git a/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js b/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
index 3f37301..c924d29 100644
--- a/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
+++ b/web/cypress/integration/plugin/create-edit-delete-plugin.spec.js
@@ -42,12 +42,9 @@ context('Enable and Delete Plugin List', () => {
 
     cy.get(this.domSelector.refresh).click();
     cy.contains('Configure').click();
-    cy.get(this.domSelector.codemirror)
-      .first()
-      .then(() => {
-        cy.get(this.domSelector.disabledSwitcher).click();
-        cy.contains('button', 'Submit').click();
-      });
+    cy.get(this.domSelector.monacoScroll).should('exist');
+    cy.get(this.domSelector.disabledSwitcher).click();
+    cy.contains('button', 'Submit').click();
   });
 
   it('should delete plugin list', function () {
diff --git a/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js b/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js
index 867baad..34ff152 100644
--- a/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js
+++ b/web/cypress/integration/plugin/create-route-with-plugin-orchestration.spec.js
@@ -36,7 +36,7 @@ context('Create and delete route with plugin orchestration', () => {
     canvasContainer: '#container',
     drawer: '.ant-drawer-content',
     deleteAlert: '.ant-modal-body',
-    codemirrorScroll: '.CodeMirror-scroll',
+    monacoScroll: '.monaco-scrollable-element',
   };
 
   beforeEach(() => {
@@ -114,7 +114,7 @@ context('Create and delete route with plugin orchestration', () => {
     cy.visit('/routes/list');
     cy.contains('routeName').siblings().contains('More').click();
     cy.contains('View').click();
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('script').should('exist');
     });
     cy.contains('Cancel').click();
diff --git a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js
index 03e5f62..2567931 100644
--- a/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js
+++ b/web/cypress/integration/pluginTemplate/create-edit-delete-plugin-template.spec.js
@@ -23,7 +23,7 @@ context('Create Configure and Delete PluginTemplate', () => {
     empty: '.ant-empty-normal',
     description: '#desc',
     drawer: '.ant-drawer-content',
-    codeMirrorMode: "[data-cy='code-mirror-mode']",
+    monacoMode: "[data-cy='monaco-mode']",
     selectDropdown: '.ant-select-dropdown',
     selectJSON: '.ant-select-dropdown [label=JSON]',
     disabledSwitcher: '#disable',
@@ -66,7 +66,7 @@ context('Create Configure and Delete PluginTemplate', () => {
     });
     cy.focused(selector.drawer).should('exist');
 
-    cy.get(selector.codeMirrorMode).click();
+    cy.get(selector.monacoMode).click();
     cy.get(selector.selectDropdown).should('be.visible');
     cy.get(selector.selectJSON).click();
     cy.get(selector.disabledSwitcher).click({
diff --git a/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js b/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js
index 9f9233b..f89b341 100644
--- a/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js
+++ b/web/cypress/integration/rawDataEditor/test-rawDataEditor.spec.js
@@ -24,6 +24,7 @@ context('Test RawDataEditor', () => {
     tableBody: '.ant-table-tbody',
     drawer: '.ant-drawer-content',
     notification: '.ant-notification-notice-message',
+    monacoViewZones: '.view-zones'
   };
 
   const data = {
@@ -55,10 +56,10 @@ context('Test RawDataEditor', () => {
 
       const dataSetItem = dataset[item];
 
-      cy.window().then(({ codemirror }) => {
-        if (codemirror) {
-          codemirror.setValue(JSON.stringify(dataSetItem));
-        }
+      cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+      // edit monaco
+      cy.window().then((window) => {
+        window.monacoEditor.setValue(JSON.stringify(dataSetItem));
         cy.get(selector.drawer).should('exist');
         cy.get(selector.drawer, { timeout }).within(() => {
           cy.contains('Submit').click({
@@ -85,13 +86,13 @@ context('Test RawDataEditor', () => {
           .click();
       }
 
-      cy.window().then(({ codemirror }) => {
-        if (codemirror) {
-          if (item === 'Consumer') {
-            codemirror.setValue(JSON.stringify({ ...dataSetItem, desc: 'newDesc' }));
-          } else {
-            codemirror.setValue(JSON.stringify({ ...dataSetItem, name: 'newName' }));
-          }
+      cy.get(selector.monacoViewZones).should('exist').click({ force: true });
+      // edit monaco
+      cy.window().then((window) => {
+        if (item === 'Consumer') {
+          window.monacoEditor.setValue(JSON.stringify({ ...dataSetItem, desc: 'newDesc' }));
+        } else {
+          window.monacoEditor.setValue(JSON.stringify({ ...dataSetItem, name: 'newName' }));
         }
         cy.get(selector.drawer).should('exist');
         cy.get(selector.drawer, { timeout }).within(() => {
diff --git a/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js b/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js
index a579471..87e3cbf 100644
--- a/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js
+++ b/web/cypress/integration/route/create-edit-duplicate-delete-route.spec.js
@@ -43,11 +43,11 @@ context('Create and Delete Route', () => {
     checkedSwitcher: '.ant-switch-checked',
     drawer: '.ant-drawer-content',
     selectDropdown: '.ant-select-dropdown',
-    codeMirrorMode: "[data-cy='code-mirror-mode']",
+    monacoMode: "[data-cy='monaco-mode']",
     selectJSON: '.ant-select-dropdown [label=JSON]',
     drawerFooter: '.ant-drawer-footer',
     nameSelector: '[title=Name]',
-    codemirrorScroll: '.CodeMirror-scroll',
+    monacoScroll: ".monaco-scrollable-element",
     deleteAlert: '.ant-modal-body',
     notificationCloseIcon: '.ant-notification-close-icon',
     notification: '.ant-notification-notice-message',
@@ -137,7 +137,7 @@ context('Create and Delete Route', () => {
         cy.get(selector.checkedSwitcher).should('exist');
       });
 
-    cy.get(selector.codeMirrorMode).click();
+    cy.get(selector.monacoMode).click();
     cy.get(selector.selectDropdown).should('be.visible');
     cy.get(selector.selectJSON).click();
 
@@ -181,7 +181,7 @@ context('Create and Delete Route', () => {
     cy.contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('upstream').should('exist');
       cy.contains('vars').should('exist')
       cy.contains(name).should('exist');
@@ -219,7 +219,7 @@ context('Create and Delete Route', () => {
     cy.contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('upstream').should('exist');
       cy.contains(newName).should('exist');
       cy.contains('vars').should('not.exist');
@@ -254,7 +254,7 @@ context('Create and Delete Route', () => {
     cy.contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('upstream').should('exist');
       cy.contains(duplicateNewName).should('exist');
     });
diff --git a/web/cypress/integration/service/create-edit-delete-service.spec.js b/web/cypress/integration/service/create-edit-delete-service.spec.js
index 96ee893..74ae8f8 100644
--- a/web/cypress/integration/service/create-edit-delete-service.spec.js
+++ b/web/cypress/integration/service/create-edit-delete-service.spec.js
@@ -29,11 +29,11 @@ context('Create and Delete Service ', () => {
     disabledSwitcher: '#disable',
     drawer: '.ant-drawer-content',
     checkedSwitcher: '.ant-switch-checked',
-    codeMirrorMode: "[data-cy='code-mirror-mode']",
+    monacoScroll: ".monaco-scrollable-element",
+    monacoMode: "[data-cy='monaco-mode']",
     selectDropdown: '.ant-select-dropdown',
     selectJSON: '.ant-select-dropdown [label=JSON]',
     drawerFooter: '.ant-drawer-footer',
-    codemirrorScroll: '.CodeMirror-scroll',
     notification: '.ant-notification-notice-message',
     nameSelector: '[title=Name]',
   };
@@ -84,7 +84,7 @@ context('Create and Delete Service ', () => {
         cy.get(selector.checkedSwitcher).should('exist');
       });
 
-    cy.get(selector.codeMirrorMode).click();
+    cy.get(selector.monacoMode).click();
     cy.get(selector.selectDropdown).should('be.visible');
     cy.get(selector.selectJSON).click();
     cy.contains('button', 'Submit').click();
@@ -122,7 +122,7 @@ context('Create and Delete Service ', () => {
     cy.contains(data.serviceName).siblings().contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('upstream').should('exist');
       cy.contains(data.serviceName).should('exist');
     });
@@ -152,7 +152,7 @@ context('Create and Delete Service ', () => {
     cy.contains(data.serviceName2).siblings().contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('upstream').should('exist');
       cy.contains(data.serviceName2).should('exist');
     });
diff --git a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
index b74467c..7d8a48d 100644
--- a/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
+++ b/web/cypress/integration/upstream/create_and_delete_upstream.spec.js
@@ -28,7 +28,7 @@ context('Create and Delete Upstream', () => {
     "nameSelector": "[title=Name]",
     "upstreamType": ".ant-select-item-option-content",
     "drawer": ".ant-drawer-content",
-    "codemirrorScroll": ".CodeMirror-scroll",
+    "monacoScroll": ".monaco-scrollable-element",
     "description": "#desc",
   }
 
@@ -76,7 +76,7 @@ context('Create and Delete Upstream', () => {
     cy.contains(data.upstreamName).siblings().contains('View').click();
     cy.get('.ant-drawer-content').should('be.visible');
 
-    cy.get('.CodeMirror-scroll').within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('nodes').should("exist");
       cy.contains('roundrobin').should('exist');
       cy.contains(data.upstreamName).should('exist');
@@ -140,7 +140,7 @@ context('Create and Delete Upstream', () => {
     cy.contains(data.upstreamName).siblings().contains('View').click();
     cy.get(selector.drawer).should('be.visible');
 
-    cy.get(selector.codemirrorScroll).within(() => {
+    cy.get(selector.monacoScroll).within(() => {
       cy.contains('nodes').should("exist");
       cy.contains('chash').should('exist');
       cy.contains(data.upstreamName).should('exist');
diff --git a/web/cypress/support/commands.js b/web/cypress/support/commands.js
index 9a8a1f9..e46ca78 100644
--- a/web/cypress/support/commands.js
+++ b/web/cypress/support/commands.js
@@ -43,8 +43,9 @@ Cypress.Commands.add('configurePlugins', (cases) => {
     switch: '#disable',
     close: '.anticon-close',
     selectDropdown: '.ant-select-dropdown',
-    codeMirrorMode: '[data-cy="code-mirror-mode"]',
-    selectJSON: '.ant-select-dropdown [label=JSON]'
+    monacoMode: '[data-cy="monaco-mode"]',
+    selectJSON: '.ant-select-dropdown [label=JSON]',
+    monacoViewZones: '.view-zones'
   };
 
   cy.get(domSelector.name, { timeout }).then(function (cards) {
@@ -68,11 +69,11 @@ Cypress.Commands.add('configurePlugins', (cases) => {
         // NOTE: wait for the Drawer to appear on the DOM
         cy.focused(domSelector.drawer).should('exist');
 
-        cy.get(domSelector.codeMirrorMode).invoke('text').then(text => {
+        cy.get(domSelector.monacoMode).invoke('text').then(text => {
           if (text === 'Form') {
             cy.wait(5000);
-            cy.get(domSelector.codeMirrorMode).should('be.visible');
-            cy.get(domSelector.codeMirrorMode).click();
+            cy.get(domSelector.monacoMode).should('be.visible');
+            cy.get(domSelector.monacoMode).click();
             cy.get(domSelector.selectDropdown).should('be.visible');
             cy.get(domSelector.selectJSON).click();
           }
@@ -84,21 +85,20 @@ Cypress.Commands.add('configurePlugins', (cases) => {
           });
         });
 
-        cy.get(domSelector.codeMirrorMode).invoke('text').then(text => {
+        cy.get(domSelector.monacoMode).invoke('text').then(text => {
           if (text === 'Form') {
             // FIXME: https://github.com/cypress-io/cypress/issues/7306
             cy.wait(5000);
-            cy.get(domSelector.codeMirrorMode).should('be.visible');
-            cy.get(domSelector.codeMirrorMode).click();
+            cy.get(domSelector.monacoMode).should('be.visible');
+            cy.get(domSelector.monacoMode).click();
             cy.get(domSelector.selectDropdown).should('be.visible');
             cy.get(domSelector.selectJSON).click();
           }
         });
-
-        cy.window().then(({ codemirror }) => {
-          if (codemirror) {
-            codemirror.setValue(JSON.stringify(data));
-          }
+        // edit monaco
+        cy.get(domSelector.monacoViewZones).should('exist').click({ force: true });
+        cy.window().then((window) => {
+          window.monacoEditor.setValue(JSON.stringify(data));
 
           cy.get(domSelector.drawer, { timeout }).within(() => {
             cy.contains('Submit').click({
diff --git a/web/package.json b/web/package.json
index 8f425a4..b840201 100644
--- a/web/package.json
+++ b/web/package.json
@@ -4,8 +4,8 @@
   "private": true,
   "description": "Dashboard for Apache APISIX",
   "scripts": {
-    "analyze": "cross-env ANALYZE=1 umi build",
-    "build": "umi build",
+    "analyze": "cross-env ANALYZE=1 yarn run build",
+    "build": "cp -R ./node_modules/monaco-editor ./public/ && umi build",
     "dev": "yarn run start:dev",
     "fetch:blocks": "pro fetch-blocks --branch antd@4 && yarn run prettier",
     "i18n-remove": "pro i18n-remove --locale=zh-CN --write",
@@ -19,15 +19,15 @@
     "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
     "prettier": "prettier -c --write \"**/*\"",
     "site": "yarn run fetch:blocks && yarn run build",
-    "start": "umi dev",
-    "start:e2e": "cross-env SERVE_ENV=test umi dev",
+    "start": "cp -R ./node_modules/monaco-editor ./public/ && umi dev",
+    "start:e2e": "cross-env SERVE_ENV=test yarn run start",
     "test:e2e": "start-server-and-test 'cross-env SERVE_ENV=test yarn start' http-get://localhost:8000 cypress:run-ci",
     "test-plugin:e2e": "start-server-and-test 'cross-env SERVE_ENV=test yarn start' http-get://localhost:8000 cypress:run-plugin-ci",
-    "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none umi dev",
-    "start:no-mock": "cross-env MOCK=none umi dev",
-    "start:no-ui": "cross-env UMI_UI=none umi dev",
-    "start:pre": "cross-env REACT_APP_ENV=pre umi dev",
-    "start:test": "cross-env REACT_APP_ENV=test MOCK=none umi dev",
+    "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none yarn run start",
+    "start:no-mock": "cross-env MOCK=none yarn run start",
+    "start:no-ui": "cross-env UMI_UI=none yarn run start",
+    "start:pre": "cross-env REACT_APP_ENV=pre yarn run start",
+    "start:test": "cross-env REACT_APP_ENV=test MOCK=none yarn run start",
     "test:component": "umi test ./src/components",
     "tsc": "tsc",
     "cypress:open": "cross-env CYPRESS_SERVE_ENV=test cypress open",
@@ -54,10 +54,10 @@
     "@ant-design/pro-table": "2.30.1",
     "@antv/x6": "^1.18.5",
     "@antv/x6-react-components": "^1.1.7",
+    "@monaco-editor/react": "^4.1.3",
     "@rjsf/antd": "2.2.0",
     "@rjsf/core": "2.2.0",
     "@types/js-yaml": "^4.0.0",
-    "@uiw/react-codemirror": "^3.0.5",
     "ajv": "^7.0.3",
     "ajv-formats": "^1.5.1",
     "antd": "^4.4.0",
@@ -129,6 +129,7 @@
     "express": "^4.17.1",
     "lint-staged": "^10.0.0",
     "mockjs": "^1.0.1-beta3",
+    "monaco-editor": "^0.24.0",
     "pinst": "^2.1.1",
     "prettier": "^2.0.1",
     "pro-download": "1.0.1",
diff --git a/web/src/components/Plugin/PluginDetail.tsx b/web/src/components/Plugin/PluginDetail.tsx
index 0028116..c4daac6 100644
--- a/web/src/components/Plugin/PluginDetail.tsx
+++ b/web/src/components/Plugin/PluginDetail.tsx
@@ -14,30 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useState } from 'react';
 import {
+  Alert,
   Button,
+  Divider,
+  Drawer,
+  Form,
+  Input,
   notification,
   PageHeader,
-  Switch,
-  Form,
+  Popconfirm,
   Select,
-  Divider,
-  Drawer,
-  Alert,
   Space,
-  Popconfirm,
-  Tooltip,
-  Input,
+  Switch,
 } from 'antd';
 import { useIntl } from 'umi';
-import CodeMirror from '@uiw/react-codemirror';
 import { js_beautify } from 'js-beautify';
 import { LinkOutlined } from '@ant-design/icons';
 import Ajv from 'ajv';
 import type { DefinedError } from 'ajv';
 import addFormats from 'ajv-formats';
 import { compact, omit } from 'lodash';
+import type { Monaco } from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
+import type {languages} from "monaco-editor";
 
 import { fetchSchema } from './service';
 import { json2yaml, yaml2json } from '../../helpers';
@@ -55,7 +56,7 @@ type Props = {
   maskClosable?: boolean;
   isEnabled?: boolean;
   onClose?: () => void;
-  onChange?: (data: any) => void;
+  onChange?: (data: PluginComponent.PluginDetailValues) => void;
 };
 
 const ajv = new Ajv();
@@ -98,26 +99,24 @@ const PluginDetail: React.FC<Props> = ({
   onChange = () => { },
 }) => {
   const { formatMessage } = useIntl();
-  enum codeMirrorModeList {
+  enum monacoModeList {
     JSON = 'JSON',
     YAML = 'YAML',
     UIForm = 'Form'
   }
   const [form] = Form.useForm();
   const [UIForm] = Form.useForm();
-  const ref = useRef<any>(null);
   const data = initialData[name] || {};
   const pluginType = pluginList.find((item) => item.name === name)?.type;
-  const [codeMirrorMode, setCodeMirrorMode] = useState<PluginComponent.CodeMirrorMode>(
-    codeMirrorModeList.JSON,
-  );
+  const [content, setContent] = useState<string>(JSON.stringify(data, null, 2));
+  const [monacoMode, setMonacoMode] = useState<PluginComponent.MonacoLanguage>(monacoModeList.JSON);
   const modeOptions: { label: string; value: string }[] = [
-    { label: codeMirrorModeList.JSON, value: codeMirrorModeList.JSON },
-    { label: codeMirrorModeList.YAML, value: codeMirrorModeList.YAML },
+    { label: monacoModeList.JSON, value: monacoModeList.JSON },
+    { label: monacoModeList.YAML, value: monacoModeList.YAML },
   ];
 
   if (PLUGIN_UI_LIST.includes(name)) {
-    modeOptions.push({ label: formatMessage({ id: 'component.plugin.form' }), value: codeMirrorModeList.UIForm });
+    modeOptions.push({ label: formatMessage({ id: 'component.plugin.form' }), value: monacoModeList.UIForm });
   }
 
   const getUIFormData = () => {
@@ -150,11 +149,48 @@ const PluginDetail: React.FC<Props> = ({
       scope: 'global',
     });
     if (PLUGIN_UI_LIST.includes(name)) {
-      setCodeMirrorMode(codeMirrorModeList.UIForm);
+      setMonacoMode(monacoModeList.UIForm);
       setUIFormData(initialData[name]);
-    };
+    }
   }, []);
 
+  const formatYaml = (yaml: string): string => {
+    const json=yaml2json(yaml,true)
+    if (json.error){
+      return yaml
+    }
+    return json2yaml(json.data).data;
+  }
+
+  const editorWillMount = (monaco: Monaco) => {
+    fetchSchema(name, schemaType).then((schema)=> {
+      const schemaConfig: languages.json.DiagnosticsOptions = {
+        validate: true,
+        schemas: [
+          {
+            // useless placeholder
+            uri: `https://apisix.apache.org/`,
+            fileMatch: ['*'],
+            schema
+          }
+        ],
+        trailingCommas: "error",
+        enableSchemaRequest: false
+      };
+      const yamlFormatProvider: languages.DocumentFormattingEditProvider = {
+        provideDocumentFormattingEdits(model) {
+          return [{
+            text: formatYaml(model.getValue()),
+            range: model.getFullModelRange()
+          }];
+        }
+      };
+      monaco.languages.registerDocumentFormattingEditProvider("yaml",yamlFormatProvider);
+      monaco.editor.getModels().forEach(model => model.updateOptions({tabSize: 2}));
+      monaco.languages.json.jsonDefaults.setDiagnosticsOptions(schemaConfig);
+    })
+  }
+
   const validateData = (pluginName: string, value: PluginComponent.Data) => {
     return fetchSchema(pluginName, schemaType).then((schema) => {
       return new Promise((resolve) => {
@@ -199,53 +235,40 @@ const PluginDetail: React.FC<Props> = ({
       });
     });
   };
-  const handleModeChange = (value: PluginComponent.CodeMirrorMode) => {
+
+  const handleModeChange = (value: PluginComponent.MonacoLanguage) => {
     switch (value) {
-      case codeMirrorModeList.JSON: {
-        if (codeMirrorMode === codeMirrorModeList.YAML) {
-          const { data: yamlData, error } = yaml2json(ref.current.editor.getValue(), true);
+      case monacoModeList.JSON: {
+        if (monacoMode === monacoModeList.YAML) {
+          const { data: yamlData, error } = yaml2json(content, true);
           if (error) {
-            notification.error({
-              message: 'Invalid Yaml data',
-            });
+            notification.error({message: formatMessage({id: 'component.global.invalidYaml'})});
             return;
           }
-          ref.current.editor.setValue(
-            js_beautify(yamlData, {
-              indent_size: 2,
-            }),
-          );
+          setContent(js_beautify(yamlData, { indent_size: 2 }))
         } else {
-          ref.current.editor.setValue(
-            js_beautify(JSON.stringify(getUIFormData()), {
-              indent_size: 2,
-            }),
-          );
+          setContent(js_beautify(JSON.stringify(getUIFormData()), { indent_size: 2 }))
         }
         break;
       }
-      case codeMirrorModeList.YAML: {
-        const { data: jsonData, error } = json2yaml(codeMirrorMode === codeMirrorModeList.JSON ? ref.current.editor.getValue() : JSON.stringify(getUIFormData()));
-
-        if (error) {
-          notification.error({
-            message: 'Invalid Json data',
-          });
+      case monacoModeList.YAML: {
+        const jsonData = monacoMode === monacoModeList.JSON ? content : JSON.stringify(getUIFormData());
+        const { data: yamlData, error } = json2yaml(jsonData);
+        if (error){
+          notification.error({ message: formatMessage({ id:'component.global.invalidJson' }) });
           return;
         }
-        ref.current.editor.setValue(jsonData);
+        setContent(yamlData);
         break;
       }
 
-      case codeMirrorModeList.UIForm: {
-        if (codeMirrorMode === codeMirrorModeList.JSON) {
-          setUIFormData(JSON.parse(ref.current.editor.getValue()));
+      case monacoModeList.UIForm: {
+        if (monacoMode === monacoModeList.JSON) {
+          setUIFormData(JSON.parse(content));
         } else {
-          const { data: yamlData, error } = yaml2json(ref.current.editor.getValue(), true);
+          const { data: yamlData, error } = yaml2json(content, true);
           if (error) {
-            notification.error({
-              message: 'Invalid Yaml data',
-            });
+            notification.error({ message: formatMessage({ id:'component.global.invalidYaml' }) });
             return;
           }
           setUIFormData(JSON.parse(yamlData));
@@ -255,26 +278,10 @@ const PluginDetail: React.FC<Props> = ({
       default:
         break;
     }
-    setCodeMirrorMode(value);
+    setMonacoMode(value);
   };
 
-  const formatCodes = () => {
-    try {
-      if (ref.current) {
-        ref.current.editor.setValue(
-          js_beautify(ref.current.editor.getValue(), {
-            indent_size: 2,
-          }),
-        );
-      }
-    } catch (error) {
-      notification.error({
-        message: 'Format failed',
-      });
-    }
-  };
-
-  const isNoConfigurationRequired = pluginType === PluginType.authentication && schemaType !== 'consumer' && (codeMirrorMode !== codeMirrorModeList.UIForm)
+  const isNoConfigurationRequired = pluginType === PluginType.authentication && schemaType !== 'consumer' && (monacoMode !== monacoModeList.UIForm);
 
   return (
     <Drawer
@@ -301,7 +308,7 @@ const PluginDetail: React.FC<Props> = ({
               onConfirm={() => {
                 onChange({
                   formData: form.getFieldsValue(),
-                  codemirrorData: {},
+                  monacoData: {},
                   shouldDelete: true,
                 });
               }}
@@ -319,21 +326,19 @@ const PluginDetail: React.FC<Props> = ({
               onClick={() => {
                 try {
                   let editorData;
-                  if (codeMirrorMode === codeMirrorModeList.JSON) {
-                    editorData = JSON.parse(ref.current?.editor.getValue());
-                  } else if (codeMirrorMode === codeMirrorModeList.YAML) {
-                    editorData = yaml2json(ref.current?.editor.getValue(), false).data;
+                  if (monacoMode === monacoModeList.JSON) {
+                    editorData = JSON.parse(content);
+                  } else if (monacoMode === monacoModeList.YAML) {
+                    editorData = yaml2json(content, false).data;
                   } else {
                     editorData = getUIFormData();
                   }
 
                   validateData(name, editorData).then((value) => {
-                    onChange({ formData: form.getFieldsValue(), codemirrorData: value });
+                    onChange({ formData: form.getFieldsValue(), monacoData: value });
                   });
                 } catch (error) {
-                  notification.error({
-                    message: 'Invalid JSON data',
-                  });
+                  notification.error({ message: formatMessage({ id:'component.global.invalidJson' }) });
                 }
               }}
             >
@@ -384,20 +389,13 @@ const PluginDetail: React.FC<Props> = ({
         ghost={false}
         extra={[
           <Select
-            defaultValue={codeMirrorModeList.JSON}
-            value={codeMirrorMode}
+            defaultValue={monacoModeList.JSON}
+            value={monacoMode}
             options={modeOptions}
-            onChange={(value: PluginComponent.CodeMirrorMode) => {
-              handleModeChange(value);
-            }}
-            data-cy='code-mirror-mode'
+            onChange={handleModeChange}
+            data-cy='monaco-mode'
             key={1}
-          ></Select>,
-          <Tooltip title={formatMessage({ id: "component.plugin.format-codes.disable" })} key={2}>
-            <Button type="primary" onClick={formatCodes} disabled={codeMirrorMode === codeMirrorModeList.UIForm}>
-              {formatMessage({ id: 'component.global.format' })}
-            </Button>
-          </Tooltip>,
+          />,
           <Button
             type="default"
             icon={<LinkOutlined />}
@@ -414,25 +412,34 @@ const PluginDetail: React.FC<Props> = ({
           </Button>
         ]}
       />
-      {Boolean(codeMirrorMode === codeMirrorModeList.UIForm) && <PluginForm name={name} form={UIForm} renderForm={!(pluginType === PluginType.authentication && schemaType !== 'consumer')} />}
-      <div style={{ display: codeMirrorMode === codeMirrorModeList.UIForm ? 'none' : 'unset' }}><CodeMirror
-        ref={(codemirror) => {
-          ref.current = codemirror;
-          if (codemirror) {
+      {Boolean(monacoMode === monacoModeList.UIForm) && <PluginForm name={name} form={UIForm} renderForm={!(pluginType === PluginType.authentication && schemaType !== 'consumer')} />}
+      <div style={{ display: monacoMode === monacoModeList.UIForm ? 'none' : 'unset' }}>
+        <Editor
+          value={content}
+          onChange={text=>{
+            if (text) {
+              setContent(text);
+            } else {
+              setContent('');
+            }
+          }}
+          language={monacoMode.toLocaleLowerCase()}
+          onMount={(editor)=>{
             // NOTE: for debug & test
             // @ts-ignore
-            window.codemirror = codemirror.editor;
-          }
-        }}
-        value={JSON.stringify(data, null, 2)}
-        options={{
-          mode: codeMirrorMode,
-          readOnly: (readonly || isNoConfigurationRequired) ? 'nocursor' : '',
-          lineWrapping: true,
-          lineNumbers: true,
-          showCursorWhenSelecting: true,
-          autofocus: true,
-        }} />
+            window.monacoEditor = editor;
+          }}
+          beforeMount={editorWillMount}
+          options={{
+            scrollbar: {
+              vertical: 'hidden',
+              horizontal: 'hidden',
+            },
+            wordWrap: "on",
+            minimap: {enabled: false},
+            readOnly: readonly,
+          }}
+        />
       </div>
     </Drawer>
   );
diff --git a/web/src/components/Plugin/PluginPage.tsx b/web/src/components/Plugin/PluginPage.tsx
index 5b50167..6ae0224 100644
--- a/web/src/components/Plugin/PluginPage.tsx
+++ b/web/src/components/Plugin/PluginPage.tsx
@@ -250,10 +250,10 @@ const PluginPage: React.FC<Props> = ({
       onClose={() => {
         setName(NEVER_EXIST_PLUGIN_FLAG);
       }}
-      onChange={({ codemirrorData, formData, shouldDelete }) => {
+      onChange={({ monacoData, formData, shouldDelete }) => {
         let newPlugins = {
           ...initialData,
-          [name]: { ...codemirrorData, disable: !formData.disable },
+          [name]: { ...monacoData, disable: !formData.disable },
         };
         if (shouldDelete === true) {
           newPlugins = omit(newPlugins, name);
diff --git a/web/src/components/Plugin/typing.d.ts b/web/src/components/Plugin/typing.d.ts
index 0fe2052..126b560 100644
--- a/web/src/components/Plugin/typing.d.ts
+++ b/web/src/components/Plugin/typing.d.ts
@@ -31,5 +31,11 @@ declare namespace PluginComponent {
 
   type ReferPage = '' | 'route' | 'consumer' | 'service' | 'plugin';
 
-  type CodeMirrorMode = 'JSON' | 'YAML' | 'Form';
+  type PluginDetailValues = {
+    formData: any;
+    monacoData: any;
+    shouldDelete?: boolean;
+  }
+
+  type MonacoLanguage = 'JSON' | 'YAML' | 'Form';
 }
diff --git a/web/src/components/PluginFlow/PluginFlow.tsx b/web/src/components/PluginFlow/PluginFlow.tsx
index f49130c..21afb12 100644
--- a/web/src/components/PluginFlow/PluginFlow.tsx
+++ b/web/src/components/PluginFlow/PluginFlow.tsx
@@ -155,12 +155,12 @@ const PluginFlow: React.FC<Props> = ({ chart, readonly = false }) => {
             onClose={() => {
               setPluginProps(DEFAULT_PLUGIN_PROPS)
             }}
-            onChange={({ formData, codemirrorData, shouldDelete }) => {
+            onChange={({ formData, monacoData, shouldDelete }) => {
               if (shouldDelete) {
                 FlowGraph.graph.removeCell(pluginProps.id)
               } else {
                 const disable = !formData.disable
-                FlowGraph.setData(pluginProps.id, { ...codemirrorData, disable })
+                FlowGraph.setData(pluginProps.id, { ...monacoData, disable })
               }
               setPluginProps(DEFAULT_PLUGIN_PROPS)
             }}
diff --git a/web/src/components/RawDataEditor/RawDataEditor.tsx b/web/src/components/RawDataEditor/RawDataEditor.tsx
index 8f9ba0d..bf8a24d 100644
--- a/web/src/components/RawDataEditor/RawDataEditor.tsx
+++ b/web/src/components/RawDataEditor/RawDataEditor.tsx
@@ -14,13 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useEffect, useRef, useState } from 'react';
-import { Button, Drawer, PageHeader, notification, Space, Select } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { Button, Drawer, notification, PageHeader, Select, Space } from 'antd';
 import { LinkOutlined } from '@ant-design/icons';
-import CodeMirror from '@uiw/react-codemirror';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import { useIntl } from 'umi';
 import { js_beautify } from 'js-beautify';
+import type {Monaco} from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
+import type {languages} from "monaco-editor";
 
 import { json2yaml, yaml2json } from '../../helpers';
 
@@ -33,78 +35,92 @@ type Props = {
   onSubmit?: (data: Record<string, any>) => void;
 };
 
-enum codeMirrorModeList {
+enum monacoLanguageList {
   JSON = 'JSON',
   YAML = 'YAML',
 }
 
 const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data = {}, onClose = () => { }, onSubmit = () => { } }) => {
-  const ref = useRef<any>(null);
   const { formatMessage } = useIntl();
-  const [codeMirrorMode, setCodeMirrorMode] = useState<PluginComponent.CodeMirrorMode>(
-    codeMirrorModeList.JSON,
+  const [monacoLanguage, setMonacoLanguage] = useState<PluginComponent.MonacoLanguage>(
+    monacoLanguageList.JSON,
   );
+  const [content, setContent] = useState('')
 
   useEffect(() => {
-    setCodeMirrorMode(codeMirrorModeList.JSON);
+    switch (monacoLanguage) {
+      case monacoLanguageList.JSON:
+        setContent(JSON.stringify(data, null, 2));
+        break;
+      case monacoLanguageList.YAML: {
+        const {data: yamlData} = json2yaml(JSON.stringify(data, null, 2));
+        setContent(yamlData)
+        break;
+      }
+      default:
+    }
+  }, [data])
+
+  useEffect(() => {
+    setMonacoLanguage(monacoLanguageList.JSON);
   }, [visible])
 
   const modeOptions = [
-    { label: codeMirrorModeList.JSON, value: codeMirrorModeList.JSON },
-    { label: codeMirrorModeList.YAML, value: codeMirrorModeList.YAML },
+    { label: monacoLanguageList.JSON, value: monacoLanguageList.JSON },
+    { label: monacoLanguageList.YAML, value: monacoLanguageList.YAML },
   ];
 
-  const handleModeChange = (value: PluginComponent.CodeMirrorMode) => {
+  const handleModeChange = (value: PluginComponent.MonacoLanguage) => {
     switch (value) {
-      case codeMirrorModeList.JSON: {
-        const { data: yamlData, error } = yaml2json(ref.current.editor.getValue(), true);
-
-        if (error) {
-          notification.error({
-            message: 'Invalid Yaml data',
-          });
-          return;
-        }
-        ref.current.editor.setValue(
-          js_beautify(yamlData, {
-            indent_size: 2,
-          }),
-        );
+      case monacoLanguageList.JSON:
+        setContent(c => {
+          const {data:jsonData,error} = yaml2json(c, true);
+          if (error){
+            notification.error({message: formatMessage({ id: 'component.global.invalidYaml' })});
+            return c;
+          }
+          return js_beautify(jsonData, {indent_size: 2});
+        });
         break;
-      }
-      case codeMirrorModeList.YAML: {
-        const { data: jsonData, error } = json2yaml(ref.current.editor.getValue());
-
-        if (error) {
-          notification.error({
-            message: 'Invalid JSON data',
-          });
-          return;
-        }
-        ref.current.editor.setValue(jsonData);
+      case monacoLanguageList.YAML:
+        setContent(c => {
+          const {data:yamlData,error} = json2yaml(c);
+          if (error){
+            notification.error({message: formatMessage({ id: 'component.global.invalidJson' })});
+            return c;
+          }
+          return yamlData;
+        });
         break;
-      }
       default:
         break;
     }
-    setCodeMirrorMode(value);
+    setMonacoLanguage(value)
   };
 
-  const formatCodes = () => {
-    try {
-      if (ref.current) {
-        ref.current.editor.setValue(
-          js_beautify(ref.current.editor.getValue(), {
-            indent_size: 2,
-          }),
-        );
-      }
-    } catch (error) {
-      notification.error({
-        message: 'Format failed',
-      });
+  const formatYaml = (yaml: string): string=> {
+    const json=yaml2json(yaml,true)
+    if (json.error){
+      return yaml;
     }
-  };
+    return json2yaml(json.data).data;
+  }
+
+  const editorWillMount = (monaco: Monaco) => {
+    const yamlFormatProvider: languages.DocumentFormattingEditProvider = {
+      provideDocumentFormattingEdits(model) {
+        return [{
+          text: formatYaml(model.getValue()),
+          range: model.getFullModelRange()
+        }];
+      }
+    };
+    monaco.languages.registerDocumentFormattingEditProvider("yaml",yamlFormatProvider);
+    monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
+      validate: true,
+      trailingCommas: "error",
+    });
+  }
 
   return (
     <>
@@ -126,14 +142,12 @@ const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data =
                 onClick={() => {
                   try {
                     const editorData =
-                      codeMirrorMode === codeMirrorModeList.JSON
-                        ? JSON.parse(ref.current?.editor.getValue())
-                        : yaml2json(ref.current?.editor.getValue(), false).data;
+                      monacoLanguage === monacoLanguageList.JSON
+                        ? JSON.parse(content)
+                        : yaml2json(content, false).data;
                     onSubmit(editorData);
                   } catch (error) {
-                    notification.error({
-                      message: 'Invalid JSON data',
-                    });
+                    notification.error({message: formatMessage({ id: 'component.global.invalidJson' })});
                   }
                 }}
               >
@@ -147,18 +161,15 @@ const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data =
           title=""
           extra={[
             <Select
-              defaultValue={codeMirrorModeList.JSON}
-              value={codeMirrorMode}
+              defaultValue={monacoLanguageList.JSON}
+              value={monacoLanguage}
               options={modeOptions}
-              onChange={(value: PluginComponent.CodeMirrorMode) => {
+              onChange={(value: PluginComponent.MonacoLanguage) => {
                 handleModeChange(value);
               }}
-              data-cy='code-mirror-mode'
+              data-cy='monaco-language'
             />,
-            <Button type="primary" onClick={formatCodes} key={2}>
-              {formatMessage({ id: 'component.global.format' })}
-            </Button>,
-            <CopyToClipboard text={JSON.stringify(data)} onCopy={(_: string, result: boolean) => {
+            <CopyToClipboard text={content} onCopy={(_: string, result: boolean) => {
               if (!result) {
                 notification.error({
                   message: formatMessage({ id: 'component.global.copyFail' }),
@@ -187,23 +198,30 @@ const RawDataEditor: React.FC<Props> = ({ visible, readonly = true, type, data =
             </Button>,
           ]}
         />
-        <CodeMirror
-          ref={(codemirror) => {
-            ref.current = codemirror;
-            if (codemirror) {
-              // NOTE: for debug & test
-              // @ts-ignore
-              window.codemirror = codemirror.editor;
+        <Editor
+          value={content}
+          onChange={text=>{
+            if (text){
+              setContent(text);
+            } else {
+              setContent('');
             }
           }}
-          value={JSON.stringify(data, null, 2)}
+          onMount={(editor)=>{
+            // NOTE: for debug & test
+            // @ts-ignore
+            window.monacoEditor = editor;
+          }}
+          beforeMount={editorWillMount}
+          language={monacoLanguage.toLocaleLowerCase()}
           options={{
-            mode: 'json-ld',
-            readOnly: readonly ? 'nocursor' : '',
-            lineWrapping: true,
-            lineNumbers: true,
-            showCursorWhenSelecting: true,
-            autofocus: true,
+            scrollbar:{
+              vertical: 'hidden',
+              horizontal: 'hidden',
+            },
+            wordWrap: "on",
+            minimap: {enabled: false},
+            readOnly: readonly,
           }}
         />
       </Drawer>
diff --git a/web/src/global.tsx b/web/src/global.tsx
index 346ea4d..e921c31 100644
--- a/web/src/global.tsx
+++ b/web/src/global.tsx
@@ -18,8 +18,17 @@ import { Button, message, notification } from 'antd';
 
 import React from 'react';
 import { formatMessage } from 'umi';
+import { loader } from '@monaco-editor/react';
+import { getLocale } from 'umi';
+
 import defaultSettings from '../config/defaultSettings';
 
+// see https://github.com/suren-atoyan/monaco-react/issues/168
+loader.config({ paths: { vs: '/monaco-editor/min/vs' } });
+if (getLocale() === 'zh-CN'){
+  loader.config({ "vs/nls": { availableLanguages: { "*": 'zh-cn' } } });
+}
+
 const { pwa } = defaultSettings;
 // if pwa is true
 if (pwa) {
diff --git a/web/src/locales/en-US/component.ts b/web/src/locales/en-US/component.ts
index 4b9eba9..71e959b 100644
--- a/web/src/locales/en-US/component.ts
+++ b/web/src/locales/en-US/component.ts
@@ -81,4 +81,7 @@ export default {
   'component.global.copy': 'Copy',
   'component.global.copySuccess': 'Copy Successfully ',
   'component.global.copyFail': 'Copy Failed',
+
+  'component.global.invalidYaml': 'Invalid Yaml data',
+  'component.global.invalidJson': 'Invalid Json data',
 };
diff --git a/web/src/locales/zh-CN/component.ts b/web/src/locales/zh-CN/component.ts
index 2acc640..112211e 100644
--- a/web/src/locales/zh-CN/component.ts
+++ b/web/src/locales/zh-CN/component.ts
@@ -77,4 +77,7 @@ export default {
   'component.global.copy': '复制',
   'component.global.copySuccess': '复制成功',
   'component.global.copyFail': '复制失败',
+
+  'component.global.invalidYaml': '无效的 Yaml 数据',
+  'component.global.invalidJson': '无效的 Json 数据',
 };
diff --git a/web/src/pages/Plugin/List.tsx b/web/src/pages/Plugin/List.tsx
index d0cbe3c..1df3f1e 100644
--- a/web/src/pages/Plugin/List.tsx
+++ b/web/src/pages/Plugin/List.tsx
@@ -125,11 +125,11 @@ const Page: React.FC = () => {
       onClose={() => {
         setVisible(false);
       }}
-      onChange={({ formData, codemirrorData, shouldDelete }) => {
+      onChange={({ formData, monacoData, shouldDelete }) => {
         const disable = !formData.disable;
         let plugins = {
           ...initialData,
-          [name]: { ...codemirrorData, disable },
+          [name]: { ...monacoData, disable },
         };
         if (shouldDelete === true) {
           plugins = omit(plugins, name);
diff --git a/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx b/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
index fa4b840..602a1fe 100644
--- a/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
+++ b/web/src/pages/Route/components/DebugViews/DebugDrawView.tsx
@@ -14,29 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, { useEffect, useState, useRef } from 'react';
+import React, { useEffect, useState } from 'react';
 import { Button, Card, Drawer, Form, Input, notification, Radio, Select, Spin, Tabs } from 'antd';
 import { useIntl } from 'umi';
-import CodeMirror from '@uiw/react-codemirror';
 import queryString from 'query-string';
 import Base64 from 'base-64';
 import urlRegexSafe from 'url-regex-safe';
 import CopyToClipboard from "react-copy-to-clipboard";
 import { CopyOutlined } from "@ant-design/icons";
+import type * as monacoEditor from "monaco-editor";
+import type { Monaco } from "@monaco-editor/react";
+import Editor from "@monaco-editor/react";
 
 import PanelSection from '@/components/PanelSection';
 
 import {
-  HTTP_METHOD_OPTION_LIST,
-  DEFAULT_DEBUG_PARAM_FORM_DATA,
-  DEFAULT_DEBUG_AUTH_FORM_DATA,
-  PROTOCOL_SUPPORTED,
+  DEBUG_BODY_MODE_SUPPORTED,
   DEBUG_BODY_TYPE_SUPPORTED,
-  DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED,
-  DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED,
+  DEBUG_RESPONSE_BODY_MODE_SUPPORTED,
   DebugBodyFormDataValueType,
+  DEFAULT_DEBUG_AUTH_FORM_DATA,
+  DEFAULT_DEBUG_PARAM_FORM_DATA,
+  HTTP_METHOD_OPTION_LIST,
+  PROTOCOL_SUPPORTED,
 } from '../../constants';
-import { DebugParamsView, AuthenticationView, DebugFormDataView } from '.';
+import { AuthenticationView, DebugFormDataView, DebugParamsView } from '.';
 import { debugRoute } from '../../service';
 import styles from './index.less';
 
@@ -56,15 +58,15 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
   const [headerForm] = Form.useForm();
   const [response, setResponse] = useState<RouteModule.debugResponse | null>()
   const [loading, setLoading] = useState(false);
-  const [codeMirrorHeight, setCodeMirrorHeight] = useState<number | string>(50);
-  const bodyCodeMirrorRef = useRef<any>(null);
+  const [body, setBody] = useState('');
+  const [height,setHeight] = useState(50);
   const [bodyType, setBodyType] = useState('none');
   const methodWithoutBody = ['GET', 'HEAD'];
-  const [responseBodyCodeMirrorMode, setResponseBodyCodeMirrorMode] = useState(
-    DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode,
+  const [responseBodyMode, setResponseBodyMode] = useState(
+    DEBUG_RESPONSE_BODY_MODE_SUPPORTED[0].mode,
   );
-  const [bodyCodeMirrorMode, setBodyCodeMirrorMode] = useState(
-    DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode,
+  const [bodyMode, setBodyCodeMode] = useState(
+    DEBUG_BODY_MODE_SUPPORTED[0].mode,
   );
 
   enum DebugBodyType {
@@ -114,21 +116,21 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
       }
       case DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput]: {
         let contentType = [''];
-        switch (bodyCodeMirrorMode) {
-          case DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[0].mode:
+        switch (bodyMode) {
+          case DEBUG_BODY_MODE_SUPPORTED[0].mode:
             contentType = ['application/json;charset=UTF-8'];
             break;
-          case DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[1].mode:
+          case DEBUG_BODY_MODE_SUPPORTED[1].mode:
             contentType = ['text/plain;charset=UTF-8'];
             break;
-          case DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED[2].mode:
+          case DEBUG_BODY_MODE_SUPPORTED[2].mode:
             contentType = ['application/xml;charset=UTF-8'];
             break;
           default: break;
         }
 
         return {
-          bodyFormData: bodyCodeMirrorRef.current.editor.getValue(),
+          bodyFormData: body,
           header: {
             'Content-type': contentType
           }
@@ -241,22 +243,34 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
         setResponse(resp);
         const contentType = resp.header["Content-Type"];
         if (contentType == null || contentType.length !== 1) {
-          setResponseBodyCodeMirrorMode("TEXT");
+          setResponseBodyMode("TEXT");
         } else if (contentType[0].toLowerCase().indexOf("json") !== -1) {
-          setResponseBodyCodeMirrorMode("JSON");
+          setResponseBodyMode("JSON");
         } else if (contentType[0].toLowerCase().indexOf("xml") !== -1) {
-          setResponseBodyCodeMirrorMode("XML");
+          setResponseBodyMode("XML");
         } else if (contentType[0].toLowerCase().indexOf("html") !== -1) {
-          setResponseBodyCodeMirrorMode("HTML");
+          setResponseBodyMode("HTML");
         } else {
-          setResponseBodyCodeMirrorMode("TEXT");
+          setResponseBodyMode("TEXT");
         }
-        setCodeMirrorHeight('auto');
       })
       .catch(() => {
         setLoading(false);
       });
   };
+
+  const handleEditorMount = (editor: monacoEditor.editor.IStandaloneCodeEditor, monaco: Monaco) => {
+    editor.onDidChangeModelDecorations(() => {
+      if (!editor.getDomNode()) {
+        return;
+      }
+      const padding = 40;
+      const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight);
+      const lineCount = editor.getModel()?.getLineCount() || 1;
+      setHeight(editor.getTopForLineNumber(lineCount + 1) + lineHeight + padding);
+    })
+  };
+
   return (
     <Drawer
       title={formatMessage({ id: 'page.route.onlineDebug' })}
@@ -321,7 +335,6 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
             onChange={(e) => {
               if (e.currentTarget.value === '') {
                 resetForms();
-                setCodeMirrorHeight(50);
               }
             }}
           />
@@ -357,12 +370,12 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
                   <Select
                     size="small"
                     onChange={(value) => {
-                      setBodyCodeMirrorMode(value);
+                      setBodyCodeMode(value);
                     }}
                     style={{ width: 100 }}
-                    defaultValue={bodyCodeMirrorMode}
+                    defaultValue={bodyMode}
                   >
-                    {DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED.map((modeObj) => (
+                    {DEBUG_BODY_MODE_SUPPORTED.map((modeObj) => (
                       <Option key={modeObj.name} value={modeObj.mode}>
                         {modeObj.name}
                       </Option>
@@ -381,23 +394,27 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
                   {bodyType === DEBUG_BODY_TYPE_SUPPORTED[DebugBodyType.RawInput] && (
                     <Form>
                       <Form.Item>
-                        <CodeMirror
-                          ref={(codemirror) => {
-                            bodyCodeMirrorRef.current = codemirror;
-                            if (codemirror) {
-                              // NOTE: for debug & test
-                              window.codeMirrorBody = codemirror.editor;
+                        <Editor
+                          value={body}
+                          language={bodyMode.toLowerCase()}
+                          onChange={(text)=>{
+                            if (text){
+                              setBody(text);
+                            } else {
+                              setBody('');
                             }
                           }}
                           height={250}
+                          beforeMount={(monaco) => {
+                            monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({validate: false});
+                          }}
                           options={{
-                            mode: bodyCodeMirrorMode,
-                            readOnly: '',
-                            lineWrapping: true,
-                            lineNumbers: true,
-                            showCursorWhenSelecting: true,
-                            autofocus: true,
-                            scrollbarStyle: null,
+                            scrollbar:{
+                              vertical: 'hidden',
+                              horizontal: 'hidden',
+                            },
+                            wordWrap: "on",
+                            minimap: {enabled: false},
                           }}
                         />
                       </Form.Item>
@@ -416,10 +433,10 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
               <TabPane tab={formatMessage({ id: 'page.route.TabPane.response' })} key="response">
                 <Select
                   disabled={response == null}
-                  value={responseBodyCodeMirrorMode}
-                  onSelect={(mode) => setResponseBodyCodeMirrorMode(mode as string)}>
+                  value={responseBodyMode}
+                  onSelect={(mode) => setResponseBodyMode(mode as string)}>
                   {
-                    DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED.map(mode => {
+                    DEBUG_RESPONSE_BODY_MODE_SUPPORTED.map(mode => {
                       return <Option value={mode.mode} key={mode.mode}>{mode.name}</Option>
                     })
                   }
@@ -441,18 +458,24 @@ const DebugDrawView: React.FC<RouteModule.DebugDrawProps> = (props) => {
                     <CopyOutlined />
                   </Button>
                 </CopyToClipboard>
-                <div id='codeMirror-response' style={{ marginTop: 16 }}>
-                  <CodeMirror
+                <div id='monaco-response' style={{ marginTop: 16 }}>
+                  <Editor
                     value={response ? response.data : ""}
-                    height={codeMirrorHeight}
+                    height={height}
+                    language={responseBodyMode.toLowerCase()}
+                    onMount={handleEditorMount}
+                    beforeMount={(monaco) => {
+                      monaco?.languages.json.jsonDefaults.setDiagnosticsOptions({validate: false});
+                    }}
                     options={{
-                      mode: responseBodyCodeMirrorMode,
-                      readOnly: 'nocursor',
-                      lineWrapping: true,
-                      lineNumbers: true,
-                      showCursorWhenSelecting: true,
-                      autofocus: true,
-                      scrollbarStyle: null,
+                      automaticLayout:true,
+                      scrollbar:{
+                        vertical: 'hidden',
+                        horizontal: 'hidden',
+                      },
+                      wordWrap: "on",
+                      minimap: {enabled: false},
+                      readOnly: true,
                     }}
                   />
                 </div>
diff --git a/web/src/pages/Route/constants.ts b/web/src/pages/Route/constants.ts
index f0aeea9..6688287 100644
--- a/web/src/pages/Route/constants.ts
+++ b/web/src/pages/Route/constants.ts
@@ -164,18 +164,17 @@ export const DEBUG_BODY_TYPE_SUPPORTED: RouteModule.DebugBodyType[] = [
   'raw input',
 ];
 
-// Note: codemirror mode: apl for text; javascript for json(need to format); xml for xml;
-export const DEBUG_BODY_CODEMIRROR_MODE_SUPPORTED = [
-  { name: 'JSON', mode: 'javascript' },
-  { name: 'TEXT', mode: 'apl' },
+export const DEBUG_BODY_MODE_SUPPORTED = [
+  { name: 'JSON', mode: 'json' },
+  { name: 'TEXT', mode: 'text' },
   { name: 'XML', mode: 'xml' },
 ];
 
-export const DEBUG_RESPONSE_BODY_CODEMIRROR_MODE_SUPPORTED = [
-  { name: 'JSON', mode: 'javascript' },
+export const DEBUG_RESPONSE_BODY_MODE_SUPPORTED = [
+  { name: 'JSON', mode: 'json' },
   { name: 'XML', mode: 'xml' },
   { name: 'HTML', mode: 'html' },
-  { name: 'TEXT', mode: 'apl' },
+  { name: 'TEXT', mode: 'text' },
 ];
 
 export const EXPORT_FILE_MIME_TYPE_SUPPORTED = ['application/json', 'application/x-yaml'];
diff --git a/web/tsconfig.json b/web/tsconfig.json
index d50aa7d..b0f8453 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -22,5 +22,9 @@
       "@@/*": ["./src/.umi/*"]
     }
   },
+  "include": [
+    "src",
+    "node_modules/monaco-editor/monaco.d.ts"
+  ],
   "exclude": ["node_modules", "build", "dist", "scripts", "src/.umi/*", "webpack", "jest"]
 }
diff --git a/web/yarn.lock b/web/yarn.lock
index 446e9c2..fcedfed 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1546,13 +1546,6 @@
     core-js-pure "^3.0.0"
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@7.11.2":
-  version "7.11.2"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
-  integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
-  dependencies:
-    regenerator-runtime "^0.13.4"
-
 "@babel/runtime@7.12.5":
   version "7.12.5"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
@@ -2193,6 +2186,22 @@
   dependencies:
     extend "3.0.2"
 
+"@monaco-editor/loader@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.npm.taobao.org/@monaco-editor/loader/download/@monaco-editor/loader-1.0.1.tgz#7068c9b07bbc65387c0e7a4df6dac0a326155905"
+  integrity sha1-cGjJsHu8ZTh8DnpN9trAoyYVWQU=
+  dependencies:
+    state-local "^1.0.6"
+
+"@monaco-editor/react@^4.1.3":
+  version "4.1.3"
+  resolved "https://registry.nlark.com/@monaco-editor/react/download/@monaco-editor/react-4.1.3.tgz#7dcaa584f2a4e8bd8f5298604f0b5368f8ebca55"
+  integrity sha1-fcqlhPKk6L2PUphgTwtTaPjrylU=
+  dependencies:
+    "@monaco-editor/loader" "^1.0.1"
+    prop-types "^15.7.2"
+    state-local "^1.0.7"
+
 "@mrmlnc/readdir-enhanced@^2.2.1":
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -3202,14 +3211,6 @@
     "@typescript-eslint/types" "4.18.0"
     eslint-visitor-keys "^2.0.0"
 
-"@uiw/react-codemirror@^3.0.5":
-  version "3.0.5"
-  resolved "https://registry.yarnpkg.com/@uiw/react-codemirror/-/react-codemirror-3.0.5.tgz#cda1ad64cb5a82b085575e316ed462db2bf320b4"
-  integrity sha512-Fwn55BYknCScDuN6MIzlFxSU3BsHHQDADcr6q46O6NBGRRFyD1UEIdN0cgitHqBCX2v1+b4CajadPChat6WVrQ==
-  dependencies:
-    "@babel/runtime" "7.11.2"
-    codemirror "5.59.0"
-
 "@umijs/ast@3.3.11":
   version "3.3.11"
   resolved "https://registry.yarnpkg.com/@umijs/ast/-/ast-3.3.11.tgz#33a32156eca99de59ad7c11cce1c91cd62d83325"
@@ -5738,11 +5739,6 @@ code-point-at@^1.0.0:
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
 
-codemirror@5.59.0:
-  version "5.59.0"
-  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.59.0.tgz#6d8132055459aabf21d04cae5cf5c430e5c57bb9"
-  integrity sha512-UGzSkCacY9z0rSpQ3wnTWRN2nvRE6foDXnJltWW8pazInR/R+3gXHrao4IFQMv/bSBvFBxt8/HPpkpKAS54x5Q==
-
 coffeeify@3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/coffeeify/-/coffeeify-3.0.1.tgz#5e2753000c50bd24c693115f33864248dd11136c"
@@ -11628,6 +11624,11 @@ moment@^2.24.0, moment@^2.25.3, moment@^2.27.0, moment@^2.29.1:
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
   integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
 
+monaco-editor@^0.24.0:
+  version "0.24.0"
+  resolved "https://registry.nlark.com/monaco-editor/download/monaco-editor-0.24.0.tgz?cache=0&sync_timestamp=1620845835330&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fmonaco-editor%2Fdownload%2Fmonaco-editor-0.24.0.tgz#990b55096bcc95d08d8d28e55264c6eb17707269"
+  integrity sha1-mQtVCWvMldCNjSjlUmTG6xdwcmk=
+
 moo@^0.5.0:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
@@ -15802,6 +15803,11 @@ start-server-and-test@^1.11.5:
     ps-tree "1.2.0"
     wait-on "5.3.0"
 
+state-local@^1.0.6, state-local@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.npm.taobao.org/state-local/download/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5"
+  integrity sha1-2lAhHQfwV0jVMAm+5GMHo32zhtU=
+
 state-toggle@^1.0.0:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe"