diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/Index.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/Index.vue
index 778d2143f6..44e823babb 100644
--- a/dbm-ui/frontend/src/views/db-manage/common/webconsole/Index.vue
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/Index.vue
@@ -11,8 +11,7 @@
         @remove-tab="handleClickClearScreen" />
       <RawSwitcher
         v-if="dbType === DBTypes.REDIS"
-        v-model="isRaw"
-        :db-type="dbType" />
+        v-model="isRaw" />
       <ClearScreen @change="handleClickClearScreen" />
       <ExportData @export="handleClickExport" />
       <UsageHelp
@@ -30,12 +29,12 @@
     </div>
     <div class="content-main">
       <KeepAlive>
-        <ConsolePanel
+        <Component
+          :is="consolePanelMap[dbType]"
           v-if="clusterInfo"
           :key="clusterInfo.id"
           ref="consolePanelRef"
-          v-model="clusterInfo"
-          :db-type="dbType"
+          :cluster="clusterInfo"
           :raw="isRaw"
           :style="currentFontConfig" />
       </KeepAlive>
@@ -59,13 +58,12 @@
   import screenfull from 'screenfull';
   import { useI18n } from 'vue-i18n';
 
-  import { queryAllTypeCluster } from '@services/source/dbbase';
-
   import { DBTypes } from '@common/const';
 
   import ClearScreen from './components/ClearScreen.vue';
-  import ClusterTabs from './components/ClusterTabs.vue';
-  import ConsolePanel from './components/console-panel/Index.vue';
+  import ClusterTabs, { type ClusterItem } from './components/ClusterTabs.vue';
+  import MysqlConsolePanel from './components/console-panel/mysql/Index.vue';
+  import RedisConsolePanel from './components/console-panel/redis/Index.vue';
   import ExportData from './components/ExportData.vue';
   import FontSetting from './components/FontSetting.vue';
   import FullScreen from './components/FullScreen.vue';
@@ -76,17 +74,21 @@
     dbType?: DBTypes;
   }
 
-  type ClusterItem = ServiceReturnType<typeof queryAllTypeCluster>[number];
-
   const props = withDefaults(defineProps<Props>(), {
     dbType: DBTypes.MYSQL,
   });
 
   const { t } = useI18n();
 
+  const consolePanelMap = {
+    [DBTypes.MYSQL]: MysqlConsolePanel,
+    [DBTypes.TENDBCLUSTER]: MysqlConsolePanel,
+    [DBTypes.REDIS]: RedisConsolePanel,
+  } as Record<DBTypes, any>;
+
   const rootRef = ref();
   const clusterTabsRef = ref();
-  const consolePanelRef = ref<InstanceType<typeof ConsolePanel>>();
+  const consolePanelRef = ref<InstanceType<typeof MysqlConsolePanel>>();
   const clusterInfo = ref<ClusterItem>();
   const currentFontConfig = ref({
     fontSize: '12px',
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/ClusterTabs.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/ClusterTabs.vue
index cf5a9dbfbb..1dca2bce9f 100644
--- a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/ClusterTabs.vue
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/ClusterTabs.vue
@@ -74,7 +74,7 @@
 
   import { messageWarn } from '@utils';
 
-  type ClusterItem = ServiceReturnType<typeof queryAllTypeCluster>[number];
+  export type ClusterItem = ServiceReturnType<typeof queryAllTypeCluster>[number];
 
   interface Props {
     dbType: DBTypes;
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/RawSwitcher.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/RawSwitcher.vue
index c169ef845a..4251283cdd 100644
--- a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/RawSwitcher.vue
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/RawSwitcher.vue
@@ -17,14 +17,6 @@
 <script lang="ts" setup>
   import { useI18n } from 'vue-i18n';
 
-  import { DBTypes } from '@common/const';
-
-  interface Props {
-    dbType: DBTypes;
-  }
-
-  const props = defineProps<Props>();
-
   const { t } = useI18n();
 
   const modelValue = defineModel<boolean | undefined>({
@@ -32,7 +24,7 @@
   });
 
   const handleRawSwitch = (value: boolean) => {
-    modelValue.value = props.dbType === DBTypes.REDIS ? value : undefined;
+    modelValue.value = value;
   };
 </script>
 
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/Index.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/ConsoleInput.vue
similarity index 56%
rename from dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/Index.vue
rename to dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/ConsoleInput.vue
index 96c9c59a79..a3c07a9cf0 100644
--- a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/Index.vue
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/ConsoleInput.vue
@@ -13,9 +13,7 @@
           <span :class="{ 'error-text': item.type === 'error' }">{{ item.message }}</span>
         </div>
         <template v-else>
-          <Component
-            :is="consoleConfig.renderMessage"
-            :data="item.message" />
+          <slot :message="item.message" />
         </template>
       </template>
     </div>
@@ -36,36 +34,54 @@
     </div>
   </div>
 </template>
+<script lang="ts">
+  // 未执行的命令
+  const noExecuteCommand: Record<number, string> = {};
+  // 已执行过的命令
+  const executedCommands: Record<number, string[]> = {};
+
+  const panelInputMap = reactive<
+    Record<
+      number,
+      Array<{
+        message: string | Record<string, string>[];
+        type: 'success' | 'error' | 'normal' | 'command' | string;
+      }>
+    >
+  >({});
+</script>
 <script setup lang="ts">
   import _ from 'lodash';
 
   import { queryAllTypeCluster, queryWebconsole } from '@services/source/dbbase';
 
-  import { DBTypes } from '@common/const';
-
   import { downloadText } from '@utils';
 
-  import RenderMysqlMessage, { validate } from './components/RenderMysqlMessage.vue';
-  import RenderRedisMessage, {
-    getInputPlaceholder as getRedisPlaceholder,
-    switchDbIndex,
-  } from './components/RenderRedisMessage.vue';
-
-  type ClusterItem = ServiceReturnType<typeof queryAllTypeCluster>[number];
+  export interface Props {
+    cluster: ServiceReturnType<typeof queryAllTypeCluster>[number];
+    placeholder?: string;
+    extParams?: Record<string, unknown>;
+    preCheck?: (value: string) => string;
+  }
 
-  interface Props {
-    modelValue: ClusterItem;
-    dbType: DBTypes;
-    raw?: boolean;
+  interface Emits {
+    (e: 'success', cmd: string, message: ServiceReturnType<typeof queryWebconsole>['query'], ...args: unknown[]): void;
   }
 
   interface Expose {
-    clearCurrentScreen: (id?: number) => void;
+    updateCommand: () => void;
     export: () => void;
     isInputed: (id?: number) => boolean;
+    clearCurrentScreen: (id?: number) => void;
   }
 
-  const props = defineProps<Props>();
+  const props = withDefaults(defineProps<Props>(), {
+    placeholder: '',
+    extParams: () => ({}),
+    preCheck: () => '',
+  });
+
+  const emits = defineEmits<Emits>();
 
   const command = ref('');
   const consolePanelRef = ref();
@@ -73,74 +89,24 @@
   const isFrozenTextarea = ref(false);
   const inputRef = ref();
   const realHeight = ref('52px');
-  const panelInputMap = reactive<
-    Record<
-      number,
-      Array<{
-        message: string | Record<string, string>[];
-        type: 'success' | 'error' | 'normal' | 'command' | string;
-      }>
-    >
-  >({});
 
-  const commandInputMap: Record<number, string[]> = {};
-  const noExecuteCommand: Record<number, string> = {};
-  let currentCommandIndex = 0;
-  let inputPlaceholder = '';
-  let baseParams: {
-    cluster_id: number;
-    cmd?: string;
-    [key: string]: unknown;
-  };
-  const configMap: Record<
-    string,
-    {
-      renderMessage: any;
-      validate?: (cmd: string) => string;
-      getInputPlaceholder?: (clusterId: number, domain: string) => string;
-      switchDbIndex?: (params: { clusterId: number; cmd: string; queryResult: string; commandInputs: string[] }) => {
-        dbIndex: number;
-        commandInputs: string[];
-      };
-    }
-  > = {
-    [DBTypes.MYSQL]: {
-      renderMessage: RenderMysqlMessage,
-      validate,
-    },
-    [DBTypes.TENDBCLUSTER]: {
-      renderMessage: RenderMysqlMessage,
-      validate,
-    },
-    [DBTypes.REDIS]: {
-      renderMessage: RenderRedisMessage,
-      getInputPlaceholder: getRedisPlaceholder,
-      switchDbIndex,
-    },
-  };
+  // 用于查找命令的索引
+  let commandIndex = 0;
 
-  const clusterId = computed(() => props.modelValue.id);
-  const consoleConfig = computed(() => configMap[props.dbType as keyof typeof configMap]);
+  const clusterId = computed(() => props.cluster.id);
+  const localPlaceholder = computed(() => props.placeholder || `${props.cluster.immute_domain} > `);
 
   watch(
     clusterId,
     () => {
       if (clusterId.value) {
-        const domain = props.modelValue.immute_domain;
-        inputPlaceholder = consoleConfig.value.getInputPlaceholder
-          ? consoleConfig.value.getInputPlaceholder(clusterId.value, domain)
-          : `${domain} > `;
-        baseParams = {
-          ...baseParams,
-          cluster_id: clusterId.value,
-        };
-        command.value = noExecuteCommand[clusterId.value] ?? inputPlaceholder;
+        command.value = localPlaceholder.value + (noExecuteCommand[clusterId.value] ?? '');
 
-        if (!commandInputMap[clusterId.value]) {
-          commandInputMap[clusterId.value] = [];
-          currentCommandIndex = 0;
+        if (!executedCommands[clusterId.value]) {
+          executedCommands[clusterId.value] = [];
+          commandIndex = 0;
         } else {
-          currentCommandIndex = commandInputMap[clusterId.value].length;
+          commandIndex = executedCommands[clusterId.value].length;
         }
 
         if (!panelInputMap[clusterId.value]) {
@@ -172,45 +138,43 @@
   // 回车输入指令
   const handleClickSendCommand = async (e: any) => {
     // 输入预处理
-    let cmd = e.target.value.trim() as string;
-    const isInputed = cmd.length > inputPlaceholder.length;
+    const inputValue = e.target.value.trim() as string;
+    const isInputed = inputValue.length > localPlaceholder.value.length;
+
+    // 截取输入的命令
+    const cmd = inputValue.substring(localPlaceholder.value.length);
+    executedCommands[clusterId.value].push(cmd);
+    commandIndex = executedCommands[clusterId.value].length;
+    command.value = localPlaceholder.value;
+
+    // 命令行渲染
     const commandLine = {
-      message: isInputed ? cmd : inputPlaceholder,
+      message: isInputed ? inputValue : localPlaceholder.value,
       type: 'command',
     };
-    commandInputMap[clusterId.value].push(cmd);
-    currentCommandIndex = commandInputMap[clusterId.value].length;
     panelInputMap[clusterId.value].push(commandLine);
-    command.value = inputPlaceholder;
+
     if (!isInputed) {
       return;
     }
 
-    // 校验语句
-    cmd = cmd.substring(inputPlaceholder.length);
-    if (consoleConfig.value.validate) {
-      const validateResult = consoleConfig.value.validate(cmd);
-      if (validateResult) {
-        const errorLine = {
-          message: validateResult,
-          type: 'error',
-        };
-        panelInputMap[clusterId.value].push(errorLine);
-        return;
-      }
+    // 语句预检
+    const preCheckResult = props.preCheck(cmd);
+    if (preCheckResult) {
+      const errorLine = {
+        message: preCheckResult,
+        type: 'error',
+      };
+      panelInputMap[clusterId.value].push(errorLine);
+      return;
     }
 
     // 开始请求
     try {
       loading.value = true;
-      if (typeof props.raw === 'boolean') {
-        baseParams = {
-          ...baseParams,
-          raw: props.raw,
-        };
-      }
       const executeResult = await queryWebconsole({
-        ...baseParams,
+        ...props.extParams,
+        cluster_id: clusterId.value,
         cmd,
       });
 
@@ -230,24 +194,7 @@
         };
         panelInputMap[clusterId.value].push(normalLine);
 
-        // 切换数据库、修改行前缀
-        const config = consoleConfig.value;
-        if (config.switchDbIndex) {
-          const { dbIndex, commandInputs } = config.switchDbIndex({
-            clusterId: clusterId.value,
-            cmd,
-            queryResult: executeResult.query as string,
-            commandInputs: commandInputMap[clusterId.value],
-          });
-          baseParams = {
-            ...baseParams,
-            db_num: dbIndex,
-          };
-          commandInputMap[clusterId.value] = commandInputs;
-          if (config.getInputPlaceholder) {
-            command.value = config.getInputPlaceholder(clusterId.value, props.modelValue.immute_domain);
-          }
-        }
+        emits('success', cmd, executeResult.query);
       }
     } finally {
       loading.value = false;
@@ -263,21 +210,21 @@
     const recentOnceInput = command.value;
     command.value = '';
     nextTick(() => {
-      command.value = isRestore ? recentOnceInput : inputPlaceholder;
+      command.value = isRestore ? recentOnceInput : localPlaceholder.value;
     });
     setTimeout(() => {
-      const cursorIndex = inputPlaceholder.length;
+      const cursorIndex = localPlaceholder.value.length;
       inputRef.value.setSelectionRange(cursorIndex, cursorIndex);
     });
   };
 
   // 输入
   const handleInputChange = (e: any) => {
-    if (inputRef.value.selectionStart === inputPlaceholder.length - 1) {
+    if (inputRef.value.selectionStart === localPlaceholder.value.length - 1) {
       restoreInput();
       return;
     }
-    if (inputRef.value.selectionStart < inputPlaceholder.length) {
+    if (inputRef.value.selectionStart < localPlaceholder.value.length) {
       restoreInput(false);
       return;
     }
@@ -290,35 +237,32 @@
 
   // 当前tab有未执行的command暂存,切换回来回显
   const handleInputBlur = () => {
-    if (command.value.length > inputPlaceholder.length) {
-      noExecuteCommand[clusterId.value] = command.value;
+    if (command.value.length > localPlaceholder.value.length) {
+      noExecuteCommand[clusterId.value] = command.value.substring(localPlaceholder.value.length);
     }
   };
 
   // 键盘 ↑ 键
   const handleClickUpBtn = () => {
-    if (commandInputMap[clusterId.value].length === 0 || currentCommandIndex === 0) {
+    if (executedCommands[clusterId.value].length === 0 || commandIndex === 0) {
       checkCursorPosition(true);
       return;
     }
 
-    currentCommandIndex = currentCommandIndex - 1;
-    command.value = commandInputMap[clusterId.value][currentCommandIndex];
+    commandIndex = commandIndex - 1;
+    command.value = localPlaceholder.value + executedCommands[clusterId.value][commandIndex];
     const cursorIndex = command.value.length;
     inputRef.value.setSelectionRange(cursorIndex, cursorIndex);
   };
 
   // 键盘 ↓ 键
   const handleClickDownBtn = () => {
-    if (
-      commandInputMap[clusterId.value].length === 0 ||
-      currentCommandIndex === commandInputMap[clusterId.value].length
-    ) {
+    if (executedCommands[clusterId.value].length === 0 || commandIndex === executedCommands[clusterId.value].length) {
       return;
     }
 
-    currentCommandIndex = currentCommandIndex + 1;
-    command.value = commandInputMap[clusterId.value][currentCommandIndex] ?? inputPlaceholder;
+    commandIndex = commandIndex + 1;
+    command.value = localPlaceholder.value + (executedCommands[clusterId.value][commandIndex] ?? '');
   };
 
   // 键盘 ← 键
@@ -328,19 +272,17 @@
 
   // 校正光标位置
   const checkCursorPosition = (isStartToTextEnd = false) => {
-    if (inputRef.value.selectionStart <= inputPlaceholder.length) {
-      const cursorIndex = isStartToTextEnd ? command.value.length : inputPlaceholder.length;
+    if (inputRef.value.selectionStart <= localPlaceholder.value.length) {
+      const cursorIndex = isStartToTextEnd ? command.value.length : localPlaceholder.value.length;
       inputRef.value.setSelectionRange(cursorIndex, cursorIndex);
     }
   };
 
   defineExpose<Expose>({
-    clearCurrentScreen(id?: number) {
-      const currentClusterId = id ?? clusterId.value;
-      panelInputMap[currentClusterId] = [];
-      commandInputMap[currentClusterId] = [];
-      noExecuteCommand[currentClusterId] = '';
-      command.value = inputPlaceholder;
+    updateCommand() {
+      nextTick(() => {
+        command.value = localPlaceholder.value;
+      });
     },
     export() {
       const lines = panelInputMap[clusterId.value].map((item) => item.message);
@@ -366,15 +308,19 @@
         }
       });
 
-      const fileName = `${props.modelValue.immute_domain}.txt`;
+      const fileName = `${props.cluster.immute_domain}.txt`;
       downloadText(fileName, exportTxt);
     },
     isInputed(id?: number) {
       const currentClusterId = id ?? clusterId.value;
-      return (
-        commandInputMap[currentClusterId]?.some((cmd) => cmd.length > inputPlaceholder.length) ||
-        noExecuteCommand[currentClusterId]?.substring(inputPlaceholder.length).length > 0
-      );
+      return executedCommands[currentClusterId]?.some(Boolean) || noExecuteCommand[currentClusterId]?.length > 0;
+    },
+    clearCurrentScreen(id?: number) {
+      const currentClusterId = id ?? clusterId.value;
+      panelInputMap[currentClusterId] = [];
+      executedCommands[currentClusterId] = [];
+      noExecuteCommand[currentClusterId] = '';
+      command.value = localPlaceholder.value;
     },
   });
 </script>
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/RenderRedisMessage.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/RenderRedisMessage.vue
deleted file mode 100644
index 625451b6eb..0000000000
--- a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/RenderRedisMessage.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<template>
-  <p
-    v-for="(row, index) in rows"
-    :key="index"
-    class="preserve-whitespace">
-    {{ row }}
-  </p>
-</template>
-<script lang="ts">
-  interface Props {
-    data: string;
-  }
-
-  // 集群所选的数据库索引
-  const clusterSelectedDbIndex: Record<number, number> = {};
-
-  // 设置当前集群id所选数据库索引
-  const setDbIndexByClusterId = (clusterId: number, value = 0) => {
-    clusterSelectedDbIndex[clusterId] = value;
-  };
-
-  // 命令行前缀
-  export const getInputPlaceholder = (clusterId: number, domain: string) => {
-    clusterSelectedDbIndex[clusterId] ??= 0;
-    return `${domain}[${clusterSelectedDbIndex[clusterId]}] > `;
-  };
-
-  // 切换数据库索引
-  export const switchDbIndex = ({
-    clusterId,
-    cmd,
-    queryResult,
-    commandInputs,
-  }: {
-    clusterId: number;
-    cmd: string;
-    queryResult: string;
-    commandInputs: string[];
-  }) => {
-    if (/^\s*select\s+.*$/.test(cmd) && /^OK/.test(queryResult)) {
-      const newDbIndex = Number(cmd.substring('select '.length));
-      setDbIndexByClusterId(clusterId, newDbIndex);
-      const newCommandInputs = commandInputs.map((item) => item.replace(/\[(\d+)\]/, `[${newDbIndex}]`));
-      return {
-        dbIndex: newDbIndex,
-        commandInputs: newCommandInputs,
-      };
-    }
-    return {
-      dbIndex: clusterSelectedDbIndex[clusterId],
-      commandInputs,
-    };
-  };
-</script>
-<script setup lang="ts">
-  const props = defineProps<Props>();
-
-  const rows = computed(() => props.data.split('\n') || []);
-</script>
-
-<style lang="less" scoped>
-  .preserve-whitespace {
-    font-family: 'Courier New', Courier, monospace; /* 等宽字体 */
-    white-space: pre;
-  }
-</style>
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/mysql/Index.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/mysql/Index.vue
new file mode 100644
index 0000000000..302aa44b1b
--- /dev/null
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/mysql/Index.vue
@@ -0,0 +1,43 @@
+<template>
+  <ConsoleInput
+    ref="consoleInputRef"
+    :cluster="cluster"
+    :pre-check="preCheck">
+    <template #default="{ message }">
+      <RenderMessage :data="message" />
+    </template>
+  </ConsoleInput>
+</template>
+
+<script setup lang="ts">
+  import { useI18n } from 'vue-i18n';
+
+  import type { queryAllTypeCluster } from '@services/source/dbbase';
+
+  import ConsoleInput from '../components/ConsoleInput.vue';
+
+  import RenderMessage from './components/RenderMessage.vue';
+
+  interface Props {
+    cluster: ServiceReturnType<typeof queryAllTypeCluster>[number];
+  }
+
+  defineProps<Props>();
+
+  const { t } = useI18n();
+
+  const consoleInputRef = ref<typeof ConsoleInput>();
+
+  const preCheck = (cmd: string) => {
+    if (/^\s*use\s+.*$/.test(cmd)) {
+      return t('暂不支持 use 语句,请使用 db.table 指定 database');
+    }
+    return '';
+  };
+
+  defineExpose({
+    isInputed: (clusterId: number) => consoleInputRef.value!.isInputed(clusterId),
+    clearCurrentScreen: (clusterId: number) => consoleInputRef.value!.clearCurrentScreen(clusterId),
+    export: () => consoleInputRef.value!.export(),
+  });
+</script>
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/RenderMysqlMessage.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/mysql/components/RenderMessage.vue
similarity index 86%
rename from dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/RenderMysqlMessage.vue
rename to dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/mysql/components/RenderMessage.vue
index 054cbf1128..b639eb2f71 100644
--- a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/components/RenderMysqlMessage.vue
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/mysql/components/RenderMessage.vue
@@ -24,21 +24,11 @@
     </table>
   </div>
 </template>
-<script lang="ts">
-  import { t } from '@locales/index';
-
+<script setup lang="ts">
   interface Props {
     data: Record<string, string>[];
   }
 
-  export const validate = (cmd: string) => {
-    if (/^\s*use\s+.*$/.test(cmd)) {
-      return t('暂不支持 use 语句,请使用 db.table 指定 database');
-    }
-    return '';
-  };
-</script>
-<script setup lang="ts">
   const props = defineProps<Props>();
 
   const headColumnList = computed(() => (props.data ? Object.keys(props.data[0]) : []));
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/redis/Index.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/redis/Index.vue
new file mode 100644
index 0000000000..562ec35670
--- /dev/null
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/redis/Index.vue
@@ -0,0 +1,49 @@
+<template>
+  <ConsoleInput
+    ref="consoleInputRef"
+    :cluster="cluster"
+    :ext-params="{
+      dbNum,
+      raw,
+    }"
+    :placeholder="placeholder"
+    @success="handleSuccess">
+    <template #default="{ message }">
+      <RenderMessage :data="message" />
+    </template>
+  </ConsoleInput>
+</template>
+
+<script setup lang="ts">
+  import type { queryAllTypeCluster, queryWebconsole } from '@services/source/dbbase';
+
+  import ConsoleInput from '../components/ConsoleInput.vue';
+
+  import RenderMessage from './components/RenderMessage.vue';
+
+  interface Props {
+    cluster: ServiceReturnType<typeof queryAllTypeCluster>[number];
+    raw: boolean;
+  }
+
+  const props = defineProps<Props>();
+
+  const consoleInputRef = ref<typeof ConsoleInput>();
+  const dbNum = ref(0);
+
+  const placeholder = computed(() => `${props.cluster.immute_domain}[${dbNum.value}] > `);
+
+  const handleSuccess = (cmd: string, message: ServiceReturnType<typeof queryWebconsole>['query']) => {
+    // 切换数据库索引
+    if (/^\s*select\s+.*$/.test(cmd) && /^OK/.test(message as string)) {
+      dbNum.value = Number(cmd.substring('select '.length));
+      consoleInputRef.value!.updateCommand();
+    }
+  };
+
+  defineExpose({
+    isInputed: (clusterId: number) => consoleInputRef.value!.isInputed(clusterId),
+    clearCurrentScreen: (clusterId: number) => consoleInputRef.value!.clearCurrentScreen(clusterId),
+    export: () => consoleInputRef.value!.export(),
+  });
+</script>
diff --git a/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/redis/components/RenderMessage.vue b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/redis/components/RenderMessage.vue
new file mode 100644
index 0000000000..10b03e8497
--- /dev/null
+++ b/dbm-ui/frontend/src/views/db-manage/common/webconsole/components/console-panel/redis/components/RenderMessage.vue
@@ -0,0 +1,25 @@
+<template>
+  <p
+    v-for="(row, index) in rows"
+    :key="index"
+    class="preserve-whitespace">
+    {{ row }}
+  </p>
+</template>
+
+<script setup lang="ts">
+  interface Props {
+    data: string;
+  }
+
+  const props = defineProps<Props>();
+
+  const rows = computed(() => props.data.split('\n') || []);
+</script>
+
+<style lang="less" scoped>
+  .preserve-whitespace {
+    font-family: 'Courier New', Courier, monospace; /* 等宽字体 */
+    white-space: pre;
+  }
+</style>