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

wuzhiguo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bigtop-manager.git


The following commit(s) were added to refs/heads/main by this push:
     new 143bfaf  BIGTOP-4234: Add chatbot on UI (#77)
143bfaf is described below

commit 143bfaf91e50fd2e6d32ee78f7df56cf201677ce
Author: Fdefined <[email protected]>
AuthorDate: Fri Sep 27 16:37:54 2024 +0800

    BIGTOP-4234: Add chatbot on UI (#77)
---
 bigtop-manager-ui/.env.development                 |   1 -
 bigtop-manager-ui/package.json                     |   5 +
 bigtop-manager-ui/pnpm-lock.yaml                   |  52 ++++
 bigtop-manager-ui/src/api/chatbot/index.ts         | 108 +++++++++
 bigtop-manager-ui/src/api/chatbot/types.ts         |  94 ++++++++
 bigtop-manager-ui/src/api/sse/index.ts             |  24 +-
 bigtop-manager-ui/src/api/sse/types.ts             |   4 +
 .../content.vue => assets/images/svg/close.svg}    |  45 +---
 .../images/svg/full-screen.svg}                    |  47 +---
 .../content.vue => assets/images/svg/history.svg}  |  49 +---
 .../content.vue => assets/images/svg/home.svg}     |  47 +---
 .../content.vue => assets/images/svg/left.svg}     |  47 +---
 bigtop-manager-ui/src/assets/images/svg/robot.svg  |  31 +++
 .../images/svg/send-disabled.svg}                  |  45 +---
 .../content.vue => assets/images/svg/send.svg}     |  45 +---
 .../content.vue => assets/images/svg/user.svg}     |  47 +---
 .../src/components/chatbot/chat-msg-item.vue       | 104 ++++++++
 .../src/components/chatbot/chat-window.vue         | 262 +++++++++++++++++++++
 .../src/components/chatbot/chatbot.vue             | 225 ++++++++++++++++++
 .../src/components/chatbot/model-selector.vue      |  64 +++++
 .../src/components/chatbot/platform-auth-form.vue  | 156 ++++++++++++
 .../components/chatbot/platform-auth-selector.vue  | 126 ++++++++++
 .../src/components/chatbot/platform-selection.vue  |  99 ++++++++
 .../src/components/chatbot/select-menu.vue         | 157 ++++++++++++
 .../src/components/chatbot/thread-selector.vue     | 157 ++++++++++++
 bigtop-manager-ui/src/components/common/index.ts   |   4 +-
 .../src/components/common/markdown-view/index.vue  | 110 +++++++++
 bigtop-manager-ui/src/composables/use-chat-bot.ts  | 241 +++++++++++++++++++
 bigtop-manager-ui/src/layouts/content.vue          |   2 +
 .../src/locales/en_US/{index.ts => ai.ts}          |  27 +--
 bigtop-manager-ui/src/locales/en_US/common.ts      |   9 +-
 bigtop-manager-ui/src/locales/en_US/index.ts       |   4 +-
 .../src/locales/{en_US/index.ts => zh_CN/ai.ts}    |  26 +-
 bigtop-manager-ui/src/locales/zh_CN/common.ts      |   8 +-
 bigtop-manager-ui/src/locales/zh_CN/index.ts       |   4 +-
 bigtop-manager-ui/src/main.ts                      |   4 +-
 bigtop-manager-ui/src/plugins/index.ts             |   3 +-
 .../styles/{function.scss => common/index.scss}    |   5 +-
 .../src/styles/{ => common}/mixins.scss            |   0
 .../src/styles/{ => common}/variables.scss         |   0
 .../src/styles/{default.css => main.css}           |   2 -
 bigtop-manager-ui/src/styles/main.scss             |  22 --
 .../src/styles/{default.css => marked.scss}        |  58 +++--
 .../src/styles/{scrollbar.css => scrollbar.scss}   |  13 +-
 .../{locales/en_US/index.ts => utils/render.ts}    |  29 ++-
 bigtop-manager-ui/vite.config.ts                   |   2 +-
 46 files changed, 2183 insertions(+), 431 deletions(-)

diff --git a/bigtop-manager-ui/.env.development 
b/bigtop-manager-ui/.env.development
index 6596210..66fd985 100644
--- a/bigtop-manager-ui/.env.development
+++ b/bigtop-manager-ui/.env.development
@@ -16,6 +16,5 @@
 NODE_ENV=development
 
 VITE_APP_BASE='/'
-#VITE_APP_BASE_URL='http://172.29.40.96:8080'
 VITE_APP_BASE_URL='http://localhost:8080'
 VITE_APP_BASE_API='/api'
diff --git a/bigtop-manager-ui/package.json b/bigtop-manager-ui/package.json
index 71035f4..2c7a53d 100644
--- a/bigtop-manager-ui/package.json
+++ b/bigtop-manager-ui/package.json
@@ -18,11 +18,16 @@
     "clipboard": "^2.0.11",
     "dayjs": "^1.11.9",
     "echarts": "^5.4.3",
+    "github-markdown-css": "^5.6.1",
+    "highlight.js": "^11.10.0",
     "lodash": "^4.17.21",
+    "marked": "^14.1.2",
+    "marked-highlight": "^2.1.4",
     "md5": "^2.3.0",
     "pinia": "^2.1.6",
     "pinia-plugin-persistedstate": "^3.2.0",
     "vue": "^3.4.37",
+    "vue-dompurify-html": "^5.1.0",
     "vue-i18n": "^9.2.2",
     "vue-router": "^4.2.4"
   },
diff --git a/bigtop-manager-ui/pnpm-lock.yaml b/bigtop-manager-ui/pnpm-lock.yaml
index 33389b3..ebdb434 100644
--- a/bigtop-manager-ui/pnpm-lock.yaml
+++ b/bigtop-manager-ui/pnpm-lock.yaml
@@ -26,9 +26,21 @@ dependencies:
   echarts:
     specifier: ^5.4.3
     version: 5.4.3
+  github-markdown-css:
+    specifier: ^5.6.1
+    version: 5.6.1
+  highlight.js:
+    specifier: ^11.10.0
+    version: 11.10.0
   lodash:
     specifier: ^4.17.21
     version: 4.17.21
+  marked:
+    specifier: ^14.1.2
+    version: 14.1.2
+  marked-highlight:
+    specifier: ^2.1.4
+    version: 2.1.4([email protected])
   md5:
     specifier: ^2.3.0
     version: 2.3.0
@@ -41,6 +53,9 @@ dependencies:
   vue:
     specifier: ^3.4.37
     version: 3.4.38([email protected])
+  vue-dompurify-html:
+    specifier: ^5.1.0
+    version: 5.1.0([email protected])
   vue-i18n:
     specifier: ^9.2.2
     version: 9.2.2([email protected])
@@ -2361,6 +2376,10 @@ packages:
       domelementtype: 2.3.0
     dev: true
 
+  /[email protected]:
+    resolution: {integrity: 
sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==}
+    dev: false
+
   /[email protected]:
     resolution: {integrity: 
sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==}
     dependencies:
@@ -3063,6 +3082,11 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /[email protected]:
+    resolution: {integrity: 
sha512-DItLFgHd+s7HQmk63YN4/TdvLeRqk1QP7pPKTTPrDTYoI5x7f/luJWSOZxesmuxBI2srHp8RDyoZd+9WF+WK8Q==}
+    engines: {node: '>=10'}
+    dev: false
+
   /[email protected]:
     resolution: {integrity: 
sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
     engines: {node: '>= 6'}
@@ -3253,6 +3277,11 @@ packages:
     hasBin: true
     dev: true
 
+  /[email protected]:
+    resolution: {integrity: 
sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==}
+    engines: {node: '>=12.0.0'}
+    dev: false
+
   /[email protected]:
     resolution: {integrity: 
sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
     dependencies:
@@ -3824,6 +3853,20 @@ packages:
       object-visit: 1.0.1
     dev: true
 
+  /[email protected]([email protected]):
+    resolution: {integrity: 
sha512-D1GOkcdzP+1dzjoColL7umojefFrASDuLeyaHS0Zr/Uo9jkr1V6vpLRCzfi1djmEaWyK0SYMFtHnpkZ+cwFT1w==}
+    peerDependencies:
+      marked: '>=4 <15'
+    dependencies:
+      marked: 14.1.2
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==}
+    engines: {node: '>= 18'}
+    hasBin: true
+    dev: false
+
   /[email protected]:
     resolution: {integrity: 
sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
     dependencies:
@@ -5741,6 +5784,15 @@ packages:
       vue: 3.4.38([email protected])
     dev: false
 
+  /[email protected]([email protected]):
+    resolution: {integrity: 
sha512-616o2/PBdOLM2bwlRWLdzeEC9NerLkwiudqNgaIJ5vBQWXec+u7Kuzh+45DtQQrids67s4pHnTnJZLVfyPMxbA==}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      dompurify: 3.1.6
+      vue: 3.4.38([email protected])
+    dev: false
+
   /[email protected]([email protected]):
     resolution: {integrity: 
sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
     engines: {node: ^14.17.0 || >=16.0.0}
diff --git a/bigtop-manager-ui/src/api/chatbot/index.ts 
b/bigtop-manager-ui/src/api/chatbot/index.ts
new file mode 100644
index 0000000..0b09857
--- /dev/null
+++ b/bigtop-manager-ui/src/api/chatbot/index.ts
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import request from '@/api/request.ts'
+import {
+  AuthorizedPlatform,
+  SupportedPlatform,
+  CredentialFormItem,
+  AuthCredentialTestParams,
+  AuthTestResult,
+  ChatThread,
+  ChatThreadCondition,
+  ChatThreadHistoryCondition,
+  ChatThreadHistoryItem,
+  ChatThreadDelCondition
+} from '@/api/chatbot/types.ts'
+
+export const getAuthorizedPlatforms = (): Promise<AuthorizedPlatform[]> => {
+  return request({
+    method: 'get',
+    url: '/chatbot/auth-platforms'
+  })
+}
+export const getSupportedPlatforms = (): Promise<SupportedPlatform[]> => {
+  return request({
+    method: 'get',
+    url: '/chatbot/platforms'
+  })
+}
+export const getCredentialFormModelOfPlatform = (
+  platformId: string | number
+): Promise<CredentialFormItem[]> => {
+  return request({
+    method: 'get',
+    url: `/chatbot/platforms/${platformId}/auth-credentials`
+  })
+}
+
+export const validateAuthCredentials = (
+  data: AuthCredentialTestParams
+): Promise<AuthTestResult> => {
+  return request({
+    method: 'post',
+    url: '/chatbot/auth-platforms',
+    data
+  })
+}
+
+export const getChatThreads = (
+  params: ChatThreadCondition
+): Promise<ChatThread[]> => {
+  return request({
+    method: 'get',
+    url: `/chatbot/auth-platforms/${params.authId}/threads`,
+    params: {
+      model: params.model
+    }
+  })
+}
+export const createChatThread = (
+  data: ChatThreadCondition
+): Promise<ChatThread> => {
+  return request({
+    method: 'post',
+    url: `/chatbot/auth-platforms/${data.authId}/threads?model=${data.model}`
+  })
+}
+export const getThreadChatHistory = (
+  params: ChatThreadHistoryCondition
+): Promise<ChatThreadHistoryItem[]> => {
+  return request({
+    method: 'get',
+    url: 
`/chatbot/auth-platforms/${params.authId}/threads/${params.threadId}/history`
+  })
+}
+
+export const delAuthorizedPlatform = (
+  authId: string | number
+): Promise<boolean> => {
+  return request({
+    method: 'delete',
+    url: `/chatbot/auth-platforms/${authId}`
+  })
+}
+export const delChatThread = (
+  params: ChatThreadDelCondition
+): Promise<boolean> => {
+  return request({
+    method: 'delete',
+    url: `/chatbot/auth-platforms/${params.authId}/threads/${params.threadId}`
+  })
+}
diff --git a/bigtop-manager-ui/src/api/chatbot/types.ts 
b/bigtop-manager-ui/src/api/chatbot/types.ts
new file mode 100644
index 0000000..37bc725
--- /dev/null
+++ b/bigtop-manager-ui/src/api/chatbot/types.ts
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface ChatbotConfig {
+  authId?: string | number
+  platformName?: string
+  supportModels?: string
+  model?: string
+  threadId?: string | number
+  threadName?: string
+  createTime?: string
+  updateTime?: string
+}
+export interface Platform {
+  id: string | number
+  platformId: string | number
+  platformName: string
+  supportModels: string
+  currModel?: string
+}
+
+export type AuthorizedPlatform = Platform
+
+export interface SupportedPlatform extends Platform {
+  id: string | number
+  name: string
+  supportModels: string
+}
+export interface CredentialFormItem {
+  name: string
+  displayName: string
+}
+export interface ChatThreadCondition {
+  authId: string | number
+  model: string
+}
+
+export interface ChatThread extends ChatThreadCondition {
+  threadId: string | number
+  threadName: string
+  createTime: string
+  updateTime: string
+}
+
+export interface AuthCredential {
+  key: string
+  value: string
+}
+
+export interface AuthCredentialTestParams {
+  platformId: string | number
+  authCredentials: AuthCredential[]
+}
+
+export interface AuthTestResult {
+  id: string | number
+  platformId: string | number
+  platformName: string
+  supportModels: string
+}
+
+export type Sender = 'USER' | 'SYSTEM' | 'AI'
+export interface ChatThreadHistoryItem {
+  sender: Sender
+  message: string
+  createTime?: string
+}
+
+export interface ChatThreadHistoryCondition {
+  authId: string | number
+  threadId: string | number
+}
+
+export interface SendChatMessageCondition extends ChatThreadHistoryCondition {
+  message: string
+}
+
+export type ChatThreadDelCondition = ChatThreadHistoryCondition
diff --git a/bigtop-manager-ui/src/api/sse/index.ts 
b/bigtop-manager-ui/src/api/sse/index.ts
index 22975e5..abcd72d 100644
--- a/bigtop-manager-ui/src/api/sse/index.ts
+++ b/bigtop-manager-ui/src/api/sse/index.ts
@@ -19,7 +19,8 @@
 
 import axios, { type AxiosProgressEvent, type CancelTokenSource } from 'axios'
 import request from '@/api/request.ts'
-import type { LogsRes } from './types'
+import type { chatMessagesRes, LogsRes } from './types'
+import type { SendChatMessageCondition } from '@/api/chatbot/types'
 
 export const getLogs = (
   clusterId: number,
@@ -40,3 +41,24 @@ export const getLogs = (
 
   return { promise, cancel: source.cancel }
 }
+export const sendChatMessage = (
+  data: SendChatMessageCondition,
+  func: Function
+): chatMessagesRes => {
+  const source: CancelTokenSource = axios.CancelToken.source()
+
+  const promise = request({
+    method: 'post',
+    url: 
`/chatbot/auth-platforms/${data.authId}/threads/${data.threadId}/talk`,
+    responseType: 'stream',
+    data: {
+      message: data.message
+    },
+    timeout: 0,
+    cancelToken: source.token,
+    onDownloadProgress: (progressEvent: AxiosProgressEvent) =>
+      func(progressEvent)
+  })
+
+  return { promise, cancel: source.cancel }
+}
diff --git a/bigtop-manager-ui/src/api/sse/types.ts 
b/bigtop-manager-ui/src/api/sse/types.ts
index 6f83d3e..7bb9a34 100644
--- a/bigtop-manager-ui/src/api/sse/types.ts
+++ b/bigtop-manager-ui/src/api/sse/types.ts
@@ -21,3 +21,7 @@ export interface LogsRes {
   promise: Promise<any>
   cancel: () => void
 }
+export interface chatMessagesRes {
+  promise: Promise<any>
+  cancel: () => void
+}
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/close.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/close.svg
index 1bde145..ec82ea9 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/close.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,7 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="20" height="20" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M8 8L40 40" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M8 40L40 8" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/full-screen.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/full-screen.svg
index 1bde145..a6b45d0 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/full-screen.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,9 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="20" height="20" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M33 6H42V15" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M42 33V42H33" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M15 42H6V33" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M6 15V6H15" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/history.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/history.svg
index 1bde145..4c40a45 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/history.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,11 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="20" height="20" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M5.81836 6.72729V14H13.0911" stroke="#333" stroke-width="4" 
stroke-linecap="round" stroke-linejoin="round" />
+  <path
+    d="M4 24C4 35.0457 12.9543 44 24 44V44C35.0457 44 44 35.0457 44 24C44 
12.9543 35.0457 4 24 4C16.598 4 10.1351 8.02111 6.67677 13.9981"
+    stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M24.005 12L24.0038 24.0088L32.4832 32.4882" stroke="#333" 
stroke-width="4" stroke-linecap="round"
+    stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/home.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/home.svg
index 1bde145..de12667 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/home.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,9 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="24" height="24" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M9 18V42H39V18L24 6L9 18Z" fill="none" stroke="#333" 
stroke-width="4" stroke-linecap="round"
+    stroke-linejoin="round" />
+  <path d="M19 29V42H29V29H19Z" fill="none" stroke="#333" stroke-width="4" 
stroke-linejoin="round" />
+  <path d="M9 42H39" stroke="#333" stroke-width="4" stroke-linecap="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/left.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/left.svg
index 1bde145..01ee633 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/left.svg
@@ -1,4 +1,5 @@
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+  <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
   ~ distributed with this work for additional information
@@ -16,43 +17,7 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="24" height="24" viewBox="0 0 48 48" fill="none"
+  xmlns="http://www.w3.org/2000/svg";>
+  <path d="M31 36L19 24L31 12" stroke="#333" stroke-width="4" 
stroke-linecap="round" stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/assets/images/svg/robot.svg 
b/bigtop-manager-ui/src/assets/images/svg/robot.svg
new file mode 100644
index 0000000..bb7b0e9
--- /dev/null
+++ b/bigtop-manager-ui/src/assets/images/svg/robot.svg
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <rect x="9" y="18" width="30" height="24" rx="2" fill="none" stroke="#333" 
stroke-width="2" />
+  <circle cx="17" cy="26" r="2" fill="#333" />
+  <circle cx="31" cy="26" r="2" fill="#333" />
+  <path
+    d="M20 32C18.8954 32 18 32.8954 18 34C18 35.1046 18.8954 36 20 36V32ZM28 
36C29.1046 36 30 35.1046 30 34C30 32.8954 29.1046 32 28 32V36ZM20 
36H28V32H20V36Z"
+    fill="#333" />
+  <path d="M24 10V18" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M4 26V34" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <path d="M44 26V34" stroke="#333" stroke-width="4" stroke-linecap="round" 
stroke-linejoin="round" />
+  <circle cx="24" cy="8" r="2" stroke="#333" stroke-width="4" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/send-disabled.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/send-disabled.svg
index 1bde145..51b2a88 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/send-disabled.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,7 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="20" height="20" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M43 5L29.7 43L22.1 25.9L5 18.3L43 5Z" stroke="#a5a2a2" 
stroke-width="4" stroke-linejoin="round" />
+  <path d="M43.0001 5L22.1001 25.9" stroke="#a5a2a2" stroke-width="4" 
stroke-linecap="round" stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/send.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/send.svg
index 1bde145..62ce314 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/send.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,7 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="20" height="20" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <path d="M43 5L29.7 43L22.1 25.9L5 18.3L43 5Z" stroke="#fff" 
stroke-width="4" stroke-linejoin="round" />
+  <path d="M43.0001 5L22.1001 25.9" stroke="#fff" stroke-width="4" 
stroke-linecap="round" stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/assets/images/svg/user.svg
similarity index 51%
copy from bigtop-manager-ui/src/layouts/content.vue
copy to bigtop-manager-ui/src/assets/images/svg/user.svg
index 1bde145..8bb9e8f 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/assets/images/svg/user.svg
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one
   ~ or more contributor license agreements.  See the NOTICE file
@@ -16,43 +17,9 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
 -->
-
-<script setup lang="ts">
-  import { HomeOutlined } from '@ant-design/icons-vue'
-</script>
-
-<template>
-  <a-layout-content class="container">
-    <a-breadcrumb class="breadcrumb">
-      <a-breadcrumb-item>
-        <home-outlined />
-      </a-breadcrumb-item>
-      <a-breadcrumb-item
-        v-for="(item, index) in $route.path.substring(1).split('/')"
-        :key="index"
-      >
-        {{ item }}
-      </a-breadcrumb-item>
-    </a-breadcrumb>
-    <div class="content">
-      <router-view />
-    </div>
-  </a-layout-content>
-</template>
-
-<style scoped lang="scss">
-  .container {
-    margin: 0 1rem;
-    min-height: auto;
-
-    .breadcrumb {
-      margin: 1rem 0;
-    }
-
-    .content {
-      padding: 1rem;
-      border-radius: 0.5rem;
-      background: #fff;
-    }
-  }
-</style>
+<svg width="24" height="24" viewBox="0 0 48 48" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+  <circle cx="24" cy="12" r="8" fill="none" stroke="#333" stroke-width="2" 
stroke-linecap="round"
+    stroke-linejoin="round" />
+  <path d="M42 44C42 34.0589 33.9411 26 24 26C14.0589 26 6 34.0589 6 44" 
stroke="#333" stroke-width="2"
+    stroke-linecap="round" stroke-linejoin="round" />
+</svg>
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/components/chatbot/chat-msg-item.vue 
b/bigtop-manager-ui/src/components/chatbot/chat-msg-item.vue
new file mode 100644
index 0000000..2d1ecb5
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/chat-msg-item.vue
@@ -0,0 +1,104 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import { computed, ref, watchEffect } from 'vue'
+  import { ChatThreadHistoryItem } from '@/api/chatbot/types'
+
+  interface Props {
+    chatItem: ChatThreadHistoryItem
+  }
+  const props = defineProps<Props>()
+  const emits = defineEmits(['updatedMsg'])
+  const message = ref('')
+  const isRight = computed(() => props.chatItem.sender === 'USER')
+
+  watchEffect(() => {
+    message.value = props.chatItem.message
+    emits('updatedMsg')
+  })
+</script>
+
+<template>
+  <div class="chat-item" :class="[isRight ? 'chat-r' : '']">
+    <div class="chat-item-avatar">
+      <section v-if="!isRight" class="chat-head">
+        <svg-icon name="robot" style="margin: 0" />
+      </section>
+      <section v-else class="chat-head">
+        <svg-icon name="user" style="margin: 0" />
+      </section>
+    </div>
+    <article class="chat-item-msg" :class="[isRight ? 'msg-r' : 'msg-l']">
+      <div class="msg-wrp">
+        <mark-view :mark-raw="message" />
+      </div>
+    </article>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+  .chat-head {
+    @include flexbox($justify: center, $align: center);
+    width: 32px;
+    height: 32px;
+    border-radius: 50%;
+    overflow: hidden;
+    background-color: #fff;
+    border: 1px solid #e8e8e8;
+  }
+
+  .chat-item {
+    flex: 1;
+    margin: 22px 0;
+    box-sizing: border-box;
+    display: flex;
+    &-avatar {
+      flex: 0 0 44px;
+      @include flexbox($justify: center);
+    }
+    &-msg {
+      display: flex;
+      width: calc(100% - 40px - 4px);
+      box-sizing: border-box;
+      .msg-wrp {
+        height: auto;
+        width: 100%;
+        padding: 8px;
+        border-radius: 8px;
+        align-items: flex-start;
+        border: 1px solid #e8e8e8;
+        background-color: #fff;
+      }
+    }
+  }
+
+  .msg-r {
+    width: auto;
+    justify-content: flex-end;
+    padding-left: 44px;
+  }
+
+  .msg-l {
+    padding-right: 44px;
+  }
+
+  .chat-r {
+    flex-direction: row-reverse;
+  }
+</style>
diff --git a/bigtop-manager-ui/src/components/chatbot/chat-window.vue 
b/bigtop-manager-ui/src/components/chatbot/chat-window.vue
new file mode 100644
index 0000000..713d203
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/chat-window.vue
@@ -0,0 +1,262 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import { scrollToBottom } from '@/utils/tools'
+  import { message } from 'ant-design-vue/es/components'
+  import { ref, computed, toRefs, watchEffect, watch, onActivated } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import type { Option } from './select-menu.vue'
+  import {
+    ChatbotConfig,
+    ChatThreadHistoryItem,
+    SendChatMessageCondition,
+    Sender
+  } from '@/api/chatbot/types'
+  import ChatMsgItem from './chat-msg-item.vue'
+  import useChatBot from '@/composables/use-chat-bot'
+
+  interface PlatformChatProps {
+    visible: boolean
+    isExpand: boolean
+    chatPayload: ChatbotConfig
+    currPage?: Option
+  }
+
+  const {
+    loading,
+    receiving,
+    messageReciver,
+    fetchSendChatMessage,
+    fetchThreadChatHistory
+  } = useChatBot()
+  const { t } = useI18n()
+  const inputText = ref('')
+  const isMessageReceived = ref(true)
+  const tempHistory = ref<ChatThreadHistoryItem[]>([])
+  const msgInputRef = ref<HTMLInputElement | null>(null)
+  const props = defineProps<PlatformChatProps>()
+  const { visible, currPage, isExpand, chatPayload } = toRefs(props)
+
+  const sendable = computed(
+    () => inputText.value != '' && isMessageReceived.value
+  )
+  const tempMsg = computed<ChatThreadHistoryItem>(() => ({
+    sender: 'AI',
+    message: messageReciver.value || '...'
+  }))
+
+  watch(isExpand, () => handleScrollToBottom())
+
+  watchEffect(async () => {
+    if (currPage.value?.nextPage === 'chat-window' && visible.value) {
+      const { authId, threadId } = chatPayload.value
+      const data = await fetchThreadChatHistory(
+        authId as string | number,
+        threadId as string | number
+      )
+      tempHistory.value = data as ChatThreadHistoryItem[]
+      loading.value = false
+      handleScrollToBottom()
+    }
+  })
+
+  const onInput = (e: Event) => {
+    inputText.value = (e.target as Element)?.textContent || ''
+  }
+
+  const reciveMessage = async () => {
+    try {
+      receiving.value = true
+      const { threadId, authId } = chatPayload.value
+      const res = await fetchSendChatMessage({
+        authId,
+        threadId,
+        message: inputText.value
+      } as SendChatMessageCondition)
+      if (res) {
+        isMessageReceived.value = res.state
+        updateThreadChatHistory('AI', res.message as string)
+        inputText.value = ''
+        receiving.value = false
+      }
+    } catch (error) {
+      console.log('error', error)
+    } finally {
+      isMessageReceived.value = true
+      handleScrollToBottom()
+    }
+  }
+
+  const updateThreadChatHistory = (sender: Sender, message: string) => {
+    tempHistory.value.push({
+      sender,
+      message,
+      createTime: new Date().toISOString()
+    })
+  }
+
+  const clearUpInputContent = () => {
+    msgInputRef.value!.innerHTML = ''
+  }
+
+  const sendMessage = () => {
+    isMessageReceived.value = false
+    if (inputText.value === '') {
+      message.warning(t('ai.empty_message'))
+      return
+    }
+    updateThreadChatHistory('USER', inputText.value as string)
+    handleScrollToBottom()
+    clearUpInputContent()
+    reciveMessage()
+  }
+
+  const handleScrollToBottom = () => {
+    scrollToBottom(document.querySelector('.chat-container') as HTMLElement)
+  }
+
+  onActivated(() => {
+    tempHistory.value = []
+    inputText.value = ''
+  })
+</script>
+
+<template>
+  <div class="platfrom-chat">
+    <section class="chat-container">
+      <chat-msg-item
+        v-for="(chatItem, index) of tempHistory"
+        :key="index"
+        :chat-item="chatItem"
+      />
+      <chat-msg-item
+        v-if="receiving"
+        :chat-item="tempMsg"
+        @updated-msg="handleScrollToBottom"
+      />
+    </section>
+    <footer>
+      <div class="msg-wrp">
+        <div
+          ref="msgInputRef"
+          class="msg-input"
+          data-placeholder="message chat"
+          :contenteditable="true"
+          @input="onInput"
+        ></div>
+        <div class="msg-input-suffix">
+          <a-button
+            :disabled="!sendable"
+            type="primary"
+            class="msg-input-send"
+            @click="sendMessage"
+          >
+            <svg-icon
+              :name="sendable ? 'send' : 'send-disabled'"
+              style="margin: 0"
+            />
+          </a-button>
+        </div>
+      </div>
+      <section>
+        {{ `${chatPayload?.platformName} ${chatPayload?.model}` }}
+      </section>
+    </footer>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+  .platfrom-chat {
+    @include flexbox($direction: column, $justify: space-between);
+    position: relative;
+    height: 100%;
+    padding: 4px !important;
+
+    .chat-container {
+      flex: 1 1 0%;
+      overflow: auto;
+      scroll-behavior: smooth;
+      background-color: #f8f8f8;
+    }
+
+    .msg-wrp {
+      @include flexbox($align: center);
+      border: 1px solid #e5e7eb;
+      border-bottom-left-radius: 8px;
+      border-bottom-right-radius: 8px;
+      padding: 6px;
+      overflow: hidden;
+      .ant-input {
+        border-color: transparent;
+        &:focus {
+          border-color: transparent;
+          box-shadow: none;
+        }
+        &:hover {
+          border-color: transparent;
+        }
+      }
+
+      .msg-input {
+        width: 100%;
+        max-height: 100px;
+        padding: 2px 8px;
+        white-space: normal;
+        transition: all 0.2s;
+        overflow: auto;
+        font-weight: 500;
+        &::before {
+          content: attr(data-placeholder);
+          color: #a9a9a9;
+          display: block;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+        }
+        &:not(:empty)::before {
+          content: '';
+        }
+        &:focus-visible {
+          outline: 0;
+          border-color: none;
+        }
+        &-suffix {
+          align-self: end;
+          cursor: pointer;
+        }
+        &-send {
+          width: 50px;
+          height: 34px;
+          @include flexbox($justify: flex-end, $align: center);
+          border-radius: 12px;
+        }
+      }
+    }
+
+    footer {
+      width: 100%;
+      section {
+        text-align: center;
+        font-size: 12px;
+        padding: 2px;
+        color: #a9a9a9;
+      }
+    }
+  }
+</style>
diff --git a/bigtop-manager-ui/src/components/chatbot/chatbot.vue 
b/bigtop-manager-ui/src/components/chatbot/chatbot.vue
new file mode 100644
index 0000000..38d6460
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/chatbot.vue
@@ -0,0 +1,225 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import { ref, computed, watchEffect, watch } from 'vue'
+  import { useSessionStorage } from '@vueuse/core'
+  import PlatformAuthSelector from './platform-auth-selector.vue'
+  import PlatformSelection from './platform-selection.vue'
+  import PlatformAuthForm from './platform-auth-form.vue'
+  import ModelSelector from './model-selector.vue'
+  import ThreadSelector from './thread-selector.vue'
+  import chatWindow from './chat-window.vue'
+  import type { Option } from './select-menu.vue'
+  import type { ChatbotConfig } from '@/api/chatbot/types'
+
+  const pages: Record<string, any> = {
+    'platform-auth-selector': PlatformAuthSelector,
+    'platform-selection': PlatformSelection,
+    'platform-auth-form': PlatformAuthForm,
+    'model-selector': ModelSelector,
+    'thread-selector': ThreadSelector,
+    'chat-window': chatWindow
+  }
+  const style: Record<string, string> = {
+    width: '100%',
+    boxSizing: 'border-box',
+    padding: '24px'
+  }
+  const withoutBack = ['chat-window', 'platform-auth-selector']
+
+  const visible = ref(false)
+  const currPage = ref<Option>({ nextPage: 'platform-auth-selector' })
+  const afterPages = ref<Option[]>([currPage.value])
+  const chatPayload = useSessionStorage<ChatbotConfig>('chatbot-payload', {})
+  const isExpand = useSessionStorage('is-expand', false)
+
+  const getCompName = computed(() => pages[currPage.value.nextPage])
+  const showChatOps = computed(() => 'chat-window' === 
currPage.value?.nextPage)
+  const showBack = computed(
+    () =>
+      withoutBack.includes(currPage.value.nextPage) ||
+      afterPages.value.length == 1
+  )
+
+  const visibleWindow = (close = false) => {
+    close ? (visible.value = false) : (visible.value = !visible.value)
+  }
+
+  const resetPageStatus = () => {
+    currPage.value = { nextPage: 'platform-auth-selector' }
+    afterPages.value = []
+  }
+
+  const onBack = () => {
+    afterPages.value.pop()
+    currPage.value = afterPages.value[afterPages.value.length - 1]
+  }
+
+  const skipStep = (nextPage: string) => {
+    currPage.value = { nextPage }
+    afterPages.value = [currPage.value]
+  }
+
+  const onFullScreen = () => {
+    isExpand.value = !isExpand.value
+  }
+
+  watch(visible, (val) => {
+    val && chatPayload.value.threadId && skipStep('chat-window')
+  })
+
+  watchEffect(() => {
+    if (visible.value) {
+      const nextPage = afterPages.value.map((v) => v?.nextPage)
+      if (!nextPage?.includes(currPage.value.nextPage)) {
+        afterPages.value?.push(currPage.value)
+      }
+      return
+    }
+    resetPageStatus()
+  })
+</script>
+
+<template>
+  <a-float-button type="default" @click="() => visibleWindow()">
+    <template #icon>
+      <div class="chatbot-icon">
+        <svg-icon name="robot" style="margin: 0" />
+      </div>
+    </template>
+  </a-float-button>
+
+  <teleport to="body">
+    <div :class="[isExpand ? 'chatbot-expand' : 'chatbot']">
+      <a-card
+        v-show="visible"
+        class="base-model"
+        :class="[isExpand ? 'chat-model-expand' : 'chat-model']"
+      >
+        <template #title>
+          <header>
+            <div class="header-left">
+              <svg-icon v-if="!showBack" name="left" @click="onBack" />
+              <svg-icon
+                v-if="showChatOps && !isExpand"
+                name="home"
+                @click="resetPageStatus"
+              />
+            </div>
+            <div v-if="showChatOps" class="header-middle">
+              {{ chatPayload?.threadName }}
+            </div>
+            <div class="header-right">
+              <template v-if="showChatOps">
+                <svg-icon name="history" @click="skipStep('thread-selector')" 
/>
+                <svg-icon name="full-screen" @click="onFullScreen" />
+              </template>
+              <svg-icon name="close" @click="visibleWindow(true)" />
+            </div>
+          </header>
+        </template>
+        <keep-alive>
+          <component
+            :is="getCompName"
+            v-bind="{ style }"
+            v-model:currPage="currPage"
+            v-model:chat-payload="chatPayload"
+            :visible="visible"
+            :is-expand="isExpand"
+          ></component>
+        </keep-alive>
+      </a-card>
+    </div>
+  </teleport>
+</template>
+
+<style lang="scss" scoped>
+  .chatbot {
+    position: fixed;
+    bottom: 10%;
+    right: 0;
+    &-icon {
+      @include flexbox($justify: center, $align: center);
+    }
+  }
+
+  .chatbot-expand {
+    position: fixed;
+  }
+
+  .chat-model-expand {
+    position: fixed;
+    width: 80%;
+    height: 70%;
+    min-width: 350px;
+    min-height: 500px;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+  }
+
+  .chat-model {
+    width: 350px;
+    height: 500px;
+    position: absolute;
+    right: 20px;
+    bottom: 0;
+  }
+
+  .base-model {
+    border-radius: 14px;
+    @include flexbox($direction: column);
+    box-shadow: rgba(0, 0, 0, 0.2) 0px 20px 30px;
+
+    :deep(.ant-card-head) {
+      padding: 0 14px;
+      min-height: 40px;
+    }
+
+    :deep(.ant-card-body) {
+      height: 100%;
+      padding: 0;
+      @include flexbox($direction: column);
+      overflow: auto;
+    }
+
+    header {
+      box-sizing: border-box;
+      display: flex;
+
+      & > div {
+        flex: 1;
+      }
+    }
+
+    .header {
+      &-left {
+        @include flexbox($align: center);
+      }
+
+      &-middle {
+        @include flexbox($justify: center, $align: center);
+      }
+
+      &-right {
+        @include flexbox($justify: flex-end, $align: center);
+      }
+    }
+  }
+</style>
diff --git a/bigtop-manager-ui/src/components/chatbot/model-selector.vue 
b/bigtop-manager-ui/src/components/chatbot/model-selector.vue
new file mode 100644
index 0000000..a15b847
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/model-selector.vue
@@ -0,0 +1,64 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import SelectMenu from './select-menu.vue'
+  import { toRefs, computed, toRaw } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import type { SelectData, Option } from './select-menu.vue'
+  import type { ChatbotConfig } from '@/api/chatbot/types'
+
+  interface PreChatProps {
+    visible: boolean
+    chatPayload: ChatbotConfig
+    currPage?: Option
+  }
+
+  const { t } = useI18n()
+  const props = defineProps<PreChatProps>()
+  const { chatPayload } = toRefs(props)
+  const emits = defineEmits(['update:currPage', 'update:chatPayload'])
+
+  const platformModel = computed<SelectData[]>(() => [
+    {
+      title: t('ai.select_model'),
+      options: (chatPayload.value?.supportModels as string)
+        .split(',')
+        .map((v) => ({ name: v, nextPage: 'thread-selector' })) as Option[]
+    }
+  ])
+
+  const onSelect = async (option: Option) => {
+    if (option.nextPage === 'thread-selector') {
+      const transformedData = {
+        ...toRaw(chatPayload.value),
+        model: option.name
+      }
+      emits('update:chatPayload', transformedData)
+    }
+    emits('update:currPage', option)
+  }
+</script>
+
+<template>
+  <div class="model-selector">
+    <select-menu :select-data="platformModel" @select="onSelect" />
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/bigtop-manager-ui/src/components/chatbot/platform-auth-form.vue 
b/bigtop-manager-ui/src/components/chatbot/platform-auth-form.vue
new file mode 100644
index 0000000..96ec999
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/platform-auth-form.vue
@@ -0,0 +1,156 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import SelectMenu from './select-menu.vue'
+  import useChatBot from '@/composables/use-chat-bot'
+  import { computed, ref, toRaw, toRefs, watchEffect } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import type { SelectData, Option } from './select-menu.vue'
+  import type { FormInstance } from 'ant-design-vue'
+  import type { ChatbotConfig, CredentialFormItem } from '@/api/chatbot/types'
+
+  interface PlatformAuthFormProps {
+    visible: boolean
+    chatPayload: ChatbotConfig
+    currPage?: Option
+  }
+  type FormState = { [key: string]: string }
+
+  const { t } = useI18n()
+  const {
+    loading,
+    checkLoading,
+    testAuthPlatform,
+    fetchCredentialFormModelOfPlatform
+  } = useChatBot()
+  const props = defineProps<PlatformAuthFormProps>()
+  const { currPage, visible, chatPayload } = toRefs(props)
+  const formRef = ref<FormInstance>()
+  const formState = ref<FormState>({})
+  const credentialFormModel = ref<CredentialFormItem[]>([])
+  const emits = defineEmits(['update:currPage', 'update:chatPayload'])
+
+  const platformAuthForm = computed<SelectData[]>(() => [
+    {
+      title: t('ai.authorizing_platform', [currPage.value?.name]),
+      options: []
+    }
+  ])
+
+  watchEffect(() => {
+    formState.value = credentialFormModel.value.reduce((acc, cur) => {
+      acc[cur.name] = ''
+      return acc
+    }, {} as FormState)
+  })
+
+  watchEffect(async () => {
+    if (currPage.value?.nextPage === 'platform-auth-form' && visible.value) {
+      const { authId } = chatPayload.value
+      const data = await fetchCredentialFormModelOfPlatform(
+        authId as string | number
+      )
+      credentialFormModel.value = data as CredentialFormItem[]
+      loading.value = false
+    }
+  })
+
+  const onSuccess = async () => {
+    const { authId } = chatPayload.value
+    const data = await testAuthPlatform(
+      authId as string | number,
+      toRaw(formState.value)
+    )
+    if (data) {
+      const { id: authId, platformName, supportModels } = data
+      emits('update:currPage', {
+        ...currPage.value,
+        nextPage: 'model-selector'
+      })
+      emits('update:chatPayload', {
+        ...toRaw(chatPayload.value),
+        ...{
+          authId,
+          platformName,
+          supportModels
+        }
+      })
+    }
+    checkLoading.value = false
+  }
+
+  const onCheck = async () => {
+    if (!formRef.value) {
+      return
+    }
+    formRef.value
+      .validate()
+      .then(onSuccess)
+      .catch((error) => {
+        console.log('error', error)
+      })
+  }
+</script>
+
+<template>
+  <div class="platform-auth-form">
+    <a-spin :spinning="loading">
+      <select-menu :select-data="platformAuthForm">
+        <template #select-custom-content>
+          <a-form ref="formRef" :model="formState" :colon="false">
+            <a-form-item
+              v-for="item in credentialFormModel"
+              :key="item.name"
+              :label="item.displayName"
+              :name="item.name"
+              :rules="[
+                {
+                  required: true,
+                  message: `Please input ${item.displayName}!`
+                }
+              ]"
+            >
+              <a-input
+                v-model:value="formState[`${item.name}`]"
+                :placeholder="`please input ${item.displayName}`"
+              />
+            </a-form-item>
+          </a-form>
+        </template>
+      </select-menu>
+    </a-spin>
+    <footer>
+      <a-button :loading="checkLoading" type="primary" @click="onCheck">{{
+        loading ? $t('common.loadingText_verifying') : $t('common.confirm')
+      }}</a-button>
+    </footer>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+  .platform-auth-form {
+    height: 100%;
+    @include flexbox($direction: column, $justify: space-between);
+  }
+  footer {
+    width: 100%;
+    @include flexbox($justify: flex-end, $align: center);
+    padding-bottom: 20px;
+  }
+</style>
diff --git 
a/bigtop-manager-ui/src/components/chatbot/platform-auth-selector.vue 
b/bigtop-manager-ui/src/components/chatbot/platform-auth-selector.vue
new file mode 100644
index 0000000..b689fd0
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/platform-auth-selector.vue
@@ -0,0 +1,126 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import SelectMenu from './select-menu.vue'
+  import useChatBot from '@/composables/use-chat-bot'
+  import { computed, h, onActivated, ref, toRaw, toRefs, watch } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { Modal } from 'ant-design-vue/es/components'
+  import { ExclamationCircleFilled } from '@ant-design/icons-vue/lib/icons'
+  import type { SelectData, Option } from './select-menu.vue'
+  import type { AuthorizedPlatform, ChatbotConfig } from '@/api/chatbot/types'
+
+  interface PlatformAuthSelectorProps {
+    visible: boolean
+    chatPayload: ChatbotConfig
+    currPage?: Option
+  }
+  const { t } = useI18n()
+  const { loading, fetchAuthorizedPlatforms, fetchDelAuthorizedPlatform } =
+    useChatBot()
+  const props = defineProps<PlatformAuthSelectorProps>()
+  const { visible, currPage, chatPayload } = toRefs(props)
+  const authorizedPlatforms = ref<AuthorizedPlatform[]>([])
+  const emits = defineEmits(['update:currPage', 'update:chatPayload'])
+
+  const formattedOptions = computed<Option[]>(() =>
+    authorizedPlatforms.value.map((v: AuthorizedPlatform) => ({
+      ...v,
+      id: v.id,
+      name: v.platformName,
+      nextPage: 'model-selector'
+    }))
+  )
+
+  watch(visible, async (val) => {
+    if (val && currPage.value?.nextPage === 'platform-auth-selector') {
+      getAllAuthorizedPlatforms()
+    }
+  })
+
+  const getAllAuthorizedPlatforms = async () => {
+    const data = await fetchAuthorizedPlatforms()
+    authorizedPlatforms.value = data as AuthorizedPlatform[]
+    loading.value = false
+  }
+
+  const platformAuthSelectors = computed<SelectData[]>(() => [
+    {
+      title: t('ai.select_authorized_platform'),
+      emptyOptionsText: t('ai.no_authorized_platform'),
+      isDeletable: true,
+      options: formattedOptions.value
+    },
+    {
+      title: t('ai.or_you_can'),
+      isDeletable: false,
+      options: [
+        {
+          nextPage: 'platform-selection',
+          name: t('ai.authorize_new_platform')
+        }
+      ]
+    }
+  ])
+
+  const onSelect = (option: Option) => {
+    if (option.nextPage === 'model-selector') {
+      const transformedData = {
+        ...toRaw(chatPayload.value),
+        authId: option.id,
+        platformName: option.name,
+        supportModels: option.supportModels
+      }
+      emits('update:chatPayload', transformedData)
+    }
+    emits('update:currPage', option)
+  }
+
+  const onRemove = (option: Option) => {
+    Modal.confirm({
+      title: t('common.delete_confirm_title'),
+      icon: h(ExclamationCircleFilled),
+      content: t('common.delete_confirm_content', [option.name]),
+      async onOk() {
+        const { id: authId } = option
+        await fetchDelAuthorizedPlatform(authId as string | number)
+        getAllAuthorizedPlatforms()
+        loading.value = false
+      }
+    })
+  }
+
+  onActivated(() => {
+    getAllAuthorizedPlatforms()
+  })
+</script>
+
+<template>
+  <div>
+    <a-spin :spinning="loading">
+      <select-menu
+        :select-data="platformAuthSelectors"
+        @select="onSelect"
+        @remove="onRemove"
+      ></select-menu>
+    </a-spin>
+  </div>
+</template>
+
+<style lang="scss" scoped></style>
diff --git a/bigtop-manager-ui/src/components/chatbot/platform-selection.vue 
b/bigtop-manager-ui/src/components/chatbot/platform-selection.vue
new file mode 100644
index 0000000..c5ddb59
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/platform-selection.vue
@@ -0,0 +1,99 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import SelectMenu from './select-menu.vue'
+  import useChatBot from '@/composables/use-chat-bot'
+  import { computed, watch, ref, toRefs, toRaw } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import type { SelectData, Option } from './select-menu.vue'
+  import type { ChatbotConfig, SupportedPlatform } from '@/api/chatbot/types'
+
+  interface PlatformSelection {
+    visible: boolean
+    chatPayload: ChatbotConfig
+    currPage?: Option
+  }
+
+  const { t } = useI18n()
+  const { loading, fetchSupportedPlatforms } = useChatBot()
+  const props = defineProps<PlatformSelection>()
+  const { visible, currPage, chatPayload } = toRefs(props)
+  const supportedPlatforms = ref<SupportedPlatform[]>([])
+  const emits = defineEmits(['update:currPage', 'update:chatPayload'])
+
+  const getSupportedPlatforms = async () => {
+    const data = await fetchSupportedPlatforms()
+    supportedPlatforms.value = data as SupportedPlatform[]
+    loading.value = false
+  }
+
+  const formattedOptions = computed<Option[]>(() => {
+    return supportedPlatforms.value.map((platform: SupportedPlatform) => ({
+      ...platform,
+      nextPage: 'platform-auth-form'
+    })) as Option[]
+  })
+
+  const platformSeletions = computed<SelectData[]>(() => [
+    {
+      title: t('ai.select_platform_to_authorize'),
+      hasDel: false,
+      options: formattedOptions.value
+    }
+  ])
+
+  watch(
+    currPage,
+    (val) => {
+      if (visible.value && val?.nextPage === 'platform-selection') {
+        getSupportedPlatforms()
+      }
+    },
+    {
+      immediate: true,
+      deep: true
+    }
+  )
+
+  const onSelect = async (option: Option) => {
+    const transformedData = {
+      ...toRaw(chatPayload.value),
+      authId: option.id,
+      platformName: option.name,
+      supportModels: option.supportModels
+    }
+    emits('update:chatPayload', transformedData)
+    emits('update:currPage', option)
+  }
+</script>
+
+<template>
+  <div class="platform-selection">
+    <a-spin :spinning="loading">
+      <select-menu :select-data="platformSeletions" @select="onSelect" />
+    </a-spin>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+  .platform-selection {
+    height: 100%;
+    @include flexbox($direction: column, $justify: space-between);
+  }
+</style>
diff --git a/bigtop-manager-ui/src/components/chatbot/select-menu.vue 
b/bigtop-manager-ui/src/components/chatbot/select-menu.vue
new file mode 100644
index 0000000..335c66d
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/select-menu.vue
@@ -0,0 +1,157 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import { CloseOutlined } from '@ant-design/icons-vue'
+  import { toRefs } from 'vue'
+
+  export type Option = {
+    nextPage: string
+    id?: string | number
+    name?: string
+    [key: string]: any
+  }
+
+  export interface SelectData {
+    title?: string
+    isDeletable?: boolean
+    emptyOptionsText?: string
+    options?: Option[]
+  }
+
+  interface SelectBoxProps {
+    selectData?: SelectData[]
+  }
+
+  interface SelectBoxEmits {
+    (event: 'select', option: Option): void
+    (event: 'remove', option: Option): void
+  }
+
+  const props = defineProps<SelectBoxProps>()
+  const emits = defineEmits<SelectBoxEmits>()
+  const { selectData } = toRefs(props)
+
+  const onRemove = (option: Option) => {
+    emits('remove', option)
+  }
+  const onSelect = (option: Option) => {
+    emits('select', option)
+  }
+</script>
+
+<template>
+  <ul class="select">
+    <li v-for="(item, index) of selectData" :key="index" class="select-item">
+      <ul>
+        <label class="select-item-label">{{ item.title }}</label>
+        <slot name="select-custom-content">
+          <div v-if="!item.options || item.options.length == 0">
+            <slot name="empty-text">
+              <div class="select-item-empty">
+                {{ item.emptyOptionsText || $t('common.no_options') }}
+              </div>
+            </slot>
+          </div>
+          <template v-else>
+            <li
+              v-for="(option, idx) of item.options"
+              :key="idx"
+              class="select-item-option"
+              @click="onSelect(option)"
+            >
+              <span>
+                {{ option.name }}
+              </span>
+              <div
+                v-show="item.isDeletable"
+                :key="idx"
+                class="select-item-del"
+                @click.stop="onRemove(option)"
+              >
+                <CloseOutlined />
+              </div>
+            </li>
+          </template>
+        </slot>
+      </ul>
+    </li>
+  </ul>
+</template>
+
+<style lang="scss" scoped>
+  ul {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+
+    li {
+      padding: 4px 6px;
+      margin-bottom: 6px;
+      border-radius: 6px;
+    }
+  }
+
+  .select {
+    width: 100%;
+    height: 100%;
+
+    &-item {
+      margin-bottom: 20px;
+
+      &-label {
+        display: block;
+        margin-bottom: 10px;
+        font-weight: 500;
+      }
+
+      &-empty {
+        padding: 10px;
+        text-align: center;
+        color: #a9a9a9;
+      }
+
+      &-option {
+        @include flexbox($justify: space-between, $align: center);
+        border: 1px solid #c9c9c9;
+        cursor: pointer;
+
+        span {
+          flex: 1;
+          text-align: center;
+        }
+
+        .select-item-del {
+          transition: opacity 0.08s ease-in-out;
+          opacity: 0;
+          flex: 0 0 14px;
+        }
+
+        &:hover {
+          background-color: rgb(0, 0, 0, 0.06);
+
+          .select-item-del {
+            display: block;
+            opacity: 1;
+          }
+        }
+      }
+    }
+  }
+</style>
diff --git a/bigtop-manager-ui/src/components/chatbot/thread-selector.vue 
b/bigtop-manager-ui/src/components/chatbot/thread-selector.vue
new file mode 100644
index 0000000..11443fc
--- /dev/null
+++ b/bigtop-manager-ui/src/components/chatbot/thread-selector.vue
@@ -0,0 +1,157 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+<script setup lang="ts">
+  import SelectMenu from './select-menu.vue'
+  import useChatBot from '@/composables/use-chat-bot'
+  import {
+    toRefs,
+    computed,
+    h,
+    ref,
+    watchEffect,
+    toRaw,
+    onActivated
+  } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { Modal } from 'ant-design-vue/es/components'
+  import { ExclamationCircleFilled } from '@ant-design/icons-vue/lib/icons'
+  import type { SelectData, Option } from './select-menu.vue'
+  import type {
+    ChatbotConfig,
+    ChatThread,
+    ChatThreadDelCondition
+  } from '@/api/chatbot/types'
+
+  interface PreChatProps {
+    visible: boolean
+    chatPayload: ChatbotConfig
+    currPage?: Option
+  }
+
+  const {
+    loading,
+    fetchChatThreads,
+    fetchCreateChatThread,
+    fetchDelChatThread
+  } = useChatBot()
+  const { t } = useI18n()
+  const props = defineProps<PreChatProps>()
+  const { currPage, visible, chatPayload } = toRefs(props)
+  const chatThreads = ref<ChatThread[]>([])
+  const emits = defineEmits(['update:currPage', 'update:chatPayload'])
+
+  const formattedOptions = computed<Option[]>(() => {
+    return chatThreads.value.map((v) => ({
+      ...v,
+      id: v.threadId,
+      name: t('ai.thread_name', [v.threadId]),
+      nextPage: 'chat-window'
+    }))
+  })
+
+  const chatThreadsSelectData = computed<SelectData[]>(() => {
+    const selectData = [
+      {
+        title: t('ai.select_thread_to_chat'),
+        isDeletable: true,
+        options: formattedOptions.value
+      },
+      {
+        title: t('ai.or_you_can'),
+        isDeletable: false,
+        options: [
+          {
+            nextPage: 'chat-window',
+            name: t('ai.create_new_thread')
+          }
+        ]
+      }
+    ]
+    return chatThreads.value.length === 10 ? [selectData[0]] : selectData
+  })
+
+  const getAllChatThreads = async () => {
+    const { authId, model } = chatPayload.value
+    const data = (await fetchChatThreads(
+      authId as string | number,
+      model as string
+    )) as ChatThread[]
+    chatThreads.value = data
+  }
+
+  watchEffect(async () => {
+    if (currPage.value?.nextPage === 'thread-selector' && visible.value) {
+      getAllChatThreads()
+    }
+  })
+
+  const onSelect = async (option: Option) => {
+    if (option.nextPage === 'chat-window' && option.id) {
+      emits('update:chatPayload', {
+        ...toRaw(chatPayload.value),
+        threadId: option.id,
+        threadName: option.name
+      })
+    } else {
+      const data = (await fetchCreateChatThread({
+        authId: chatPayload.value.authId as string | number,
+        model: chatPayload.value.model as string
+      })) as ChatThread
+      emits('update:chatPayload', {
+        ...toRaw(chatPayload.value),
+        ...data,
+        threadName: `Thread ${data.threadId}`
+      })
+    }
+    emits('update:currPage', option)
+  }
+
+  const onRemove = (option: Option) => {
+    Modal.confirm({
+      title: t('common.delete_confirm_title'),
+      icon: h(ExclamationCircleFilled),
+      content: t('common.delete_confirm_content', [option.name]),
+      async onOk() {
+        await fetchDelChatThread({
+          threadId: option.id,
+          authId: chatPayload.value.authId
+        } as ChatThreadDelCondition)
+        getAllChatThreads()
+      }
+    })
+  }
+
+  onActivated(() => {
+    chatThreads.value = []
+  })
+</script>
+
+<template>
+  <div>
+    <a-spin :spinning="loading">
+      <select-menu
+        :select-data="chatThreadsSelectData"
+        @remove="onRemove"
+        @select="onSelect"
+      />
+    </a-spin>
+  </div>
+</template>
+
+<style scoped></style>
diff --git a/bigtop-manager-ui/src/components/common/index.ts 
b/bigtop-manager-ui/src/components/common/index.ts
index ef7b24c..d0b6fcc 100644
--- a/bigtop-manager-ui/src/components/common/index.ts
+++ b/bigtop-manager-ui/src/components/common/index.ts
@@ -20,10 +20,12 @@
 import type { App } from 'vue'
 import SvgIcon from './svg-icon/index.vue'
 import DotState from './dot-state/index.vue'
+import MarkView from './markdown-view/index.vue'
 
 const comments = {
   SvgIcon,
-  DotState
+  DotState,
+  MarkView
 }
 
 const install = (app: App) => {
diff --git a/bigtop-manager-ui/src/components/common/markdown-view/index.vue 
b/bigtop-manager-ui/src/components/common/markdown-view/index.vue
new file mode 100644
index 0000000..573f6b4
--- /dev/null
+++ b/bigtop-manager-ui/src/components/common/markdown-view/index.vue
@@ -0,0 +1,110 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+-->
+
+<script setup lang="ts">
+  import { computed, toRefs, nextTick } from 'vue'
+  import { Marked } from 'marked'
+  import { markedHighlight } from 'marked-highlight'
+  import { copyText } from '@/utils/tools'
+  import { message } from 'ant-design-vue'
+  import { useI18n } from 'vue-i18n'
+  import hljs from 'highlight.js'
+
+  type Props = {
+    markRaw: string
+  }
+
+  const props = withDefaults(defineProps<Props>(), {
+    markRaw: ''
+  })
+
+  const { t } = useI18n()
+  const { markRaw } = toRefs(props)
+  const markdownContent = computed(() => parseMDByHighlight(markRaw.value))
+
+  const copyCode = (code: string) => {
+    copyText(code)
+      .then(() => {
+        message.success(`${t('common.copy_success')}`)
+      })
+      .catch((err: Error) => {
+        message.error(`${t('common.copy_fail')}`)
+        console.log('err :>> ', err)
+      })
+  }
+
+  const upgradeCodeBlock = async (el: HTMLElement) => {
+    await nextTick()
+    const pres = el.querySelectorAll('pre')
+    pres.forEach((pre) => {
+      const code = pre.querySelector('code')
+      const langTag = pre.querySelector('#language')
+      const copyTag = pre.querySelector('#copy')
+
+      langTag!.textContent = (
+        code?.classList.value.replace('hljs language-', '') as string
+      ).toLowerCase()
+      copyTag &&
+        copyTag.removeEventListener('click', () =>
+          copyCode(code?.textContent || '')
+        )
+      copyTag?.addEventListener('click', () =>
+        copyCode(code?.textContent || '')
+      )
+    })
+  }
+
+  const upgradedCodeBlock = (parsedMarked: string) => {
+    const upgradeParts = `<pre style="padding:0"><div 
class="code-block-head"><label id="language">Code</label><button id="copy">${t(
+      'common.copy'
+    )}</button></div>`
+    return parsedMarked.replace(/<pre>/g, upgradeParts)
+  }
+
+  const getMarkedHighlightOps = () => {
+    return markedHighlight({
+      langPrefix: 'hljs language-',
+      highlight(code, lang) {
+        const language = hljs.getLanguage(lang) ? lang : 'plaintext'
+        return hljs.highlight(code, { language }).value
+      }
+    })
+  }
+
+  const parseMDByHighlight = (content: string) => {
+    const marked = new Marked(getMarkedHighlightOps())
+    return upgradedCodeBlock(marked.parse(content) as string)
+  }
+
+  const vUpgradeCodeBlock = {
+    updated: upgradeCodeBlock,
+    mounted: upgradeCodeBlock
+  }
+</script>
+
+<template>
+  <div
+    v-upgradeCodeBlock
+    v-dompurify-html="markdownContent"
+    class="markdown-body"
+  >
+  </div>
+</template>
+
+<style lang="scss" scoped></style>
diff --git a/bigtop-manager-ui/src/composables/use-chat-bot.ts 
b/bigtop-manager-ui/src/composables/use-chat-bot.ts
new file mode 100644
index 0000000..227b373
--- /dev/null
+++ b/bigtop-manager-ui/src/composables/use-chat-bot.ts
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { ref } from 'vue'
+import {
+  createChatThread,
+  delAuthorizedPlatform,
+  delChatThread,
+  getAuthorizedPlatforms,
+  getChatThreads,
+  getCredentialFormModelOfPlatform,
+  getSupportedPlatforms,
+  getThreadChatHistory,
+  validateAuthCredentials
+} from '@/api/chatbot/index'
+import type {
+  ChatThreadCondition,
+  AuthCredential,
+  ChatThreadDelCondition,
+  SendChatMessageCondition
+} from '@/api/chatbot/types'
+
+import { sendChatMessage } from '@/api/sse'
+import { AxiosProgressEvent, Canceler } from 'axios'
+import { useI18n } from 'vue-i18n'
+import { message } from 'ant-design-vue'
+
+const useChatBot = () => {
+  const { t } = useI18n()
+  const loading = ref(false)
+  const receiving = ref(false)
+  const checkLoading = ref(false)
+  const canceler = ref<Canceler>()
+  const messageReciver = ref('')
+
+  function formatAuthCredentials<T extends Object>(
+    authFormData: T
+  ): AuthCredential[] {
+    const authCredentials: AuthCredential[] = []
+    for (const [key, value] of Object.entries(authFormData)) {
+      authCredentials.push({ key, value } as AuthCredential)
+    }
+    return authCredentials
+  }
+
+  async function fetchAuthorizedPlatforms() {
+    try {
+      loading.value = true
+      const data = await getAuthorizedPlatforms()
+      return Promise.resolve(data)
+    } catch (error) {
+      console.log('error :>> ', error)
+      return []
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function fetchSupportedPlatforms() {
+    try {
+      loading.value = true
+      const data = await getSupportedPlatforms()
+      return Promise.resolve(data)
+    } catch (error) {
+      console.log('error :>> ', error)
+      return []
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function fetchCredentialFormModelOfPlatform(
+    platformId: string | number
+  ) {
+    try {
+      loading.value = true
+      const data = await getCredentialFormModelOfPlatform(platformId)
+      return Promise.resolve(data)
+    } catch (error) {
+      console.log('error :>> ', error)
+      return []
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function testAuthPlatform<T extends Object>(
+    platformId: string | number,
+    authFormData: T
+  ) {
+    try {
+      checkLoading.value = true
+      const data = await validateAuthCredentials({
+        platformId,
+        authCredentials: formatAuthCredentials<T>(authFormData)
+      })
+      return Promise.resolve(data)
+    } catch (error) {
+      checkLoading.value = false
+      console.log('error :>> ', error)
+    }
+  }
+
+  async function fetchChatThreads(authId: string | number, model: string) {
+    try {
+      loading.value = true
+      const data = await getChatThreads({
+        model,
+        authId
+      })
+      return Promise.resolve(data)
+    } catch (error) {
+      console.log('error :>> ', error)
+      return []
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function fetchCreateChatThread(platfromInfo: ChatThreadCondition) {
+    try {
+      loading.value = true
+      const data = await createChatThread(platfromInfo)
+      return Promise.resolve(data)
+    } catch (error) {
+      console.log('error :>> ', error)
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function fetchThreadChatHistory(
+    authId: string | number,
+    threadId: string | number
+  ) {
+    try {
+      loading.value = true
+      const data = await getThreadChatHistory({ authId, threadId })
+      return Promise.resolve(data)
+    } catch (error) {
+      console.log('error :>> ', error)
+      return []
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function fetchSendChatMessage(reqparams: SendChatMessageCondition) {
+    try {
+      const { cancel, promise } = sendChatMessage(reqparams, onMessageReceive)
+      canceler.value = cancel
+      return promise.then(onMessageComplete)
+    } catch (error) {
+      console.log('error :>> ', error)
+    }
+  }
+
+  async function fetchDelAuthorizedPlatform(platformId: string | number) {
+    try {
+      loading.value = true
+      const data = await delAuthorizedPlatform(platformId)
+      message[`${data ? 'success' : 'error'}`](
+        data ? t('common.delete_success') : t('common.delete_fail')
+      )
+    } catch (error) {
+      console.log('error :>> ', error)
+    } finally {
+      loading.value = false
+    }
+  }
+  async function fetchDelChatThread(payload: ChatThreadDelCondition) {
+    try {
+      loading.value = true
+      const data = await delChatThread(payload)
+      message[`${data ? 'success' : 'error'}`](
+        data ? t('common.delete_success') : t('common.delete_fail')
+      )
+    } catch (error) {
+      console.log('error :>> ', error)
+    } finally {
+      loading.value = false
+    }
+  }
+
+  const onMessageReceive = ({ event }: AxiosProgressEvent) => {
+    messageReciver.value = event.target.responseText
+      .split('data:')
+      .map((s: string) => {
+        return s.trimEnd()
+      })
+      .join('')
+  }
+
+  const onMessageComplete = () => {
+    const formatResultMsg = messageReciver.value
+    messageReciver.value = ''
+    cancelSseConnect()
+    return Promise.resolve({ message: formatResultMsg, state: true })
+  }
+
+  const cancelSseConnect = () => {
+    if (!canceler.value) {
+      return
+    }
+    canceler.value()
+  }
+
+  return {
+    messageReciver,
+    loading,
+    receiving,
+    checkLoading,
+    fetchSupportedPlatforms,
+    fetchCredentialFormModelOfPlatform,
+    testAuthPlatform,
+    fetchCreateChatThread,
+    fetchChatThreads,
+    fetchAuthorizedPlatforms,
+    fetchThreadChatHistory,
+    fetchSendChatMessage,
+    fetchDelAuthorizedPlatform,
+    fetchDelChatThread
+  }
+}
+
+export default useChatBot
diff --git a/bigtop-manager-ui/src/layouts/content.vue 
b/bigtop-manager-ui/src/layouts/content.vue
index 1bde145..1093d18 100644
--- a/bigtop-manager-ui/src/layouts/content.vue
+++ b/bigtop-manager-ui/src/layouts/content.vue
@@ -19,6 +19,7 @@
 
 <script setup lang="ts">
   import { HomeOutlined } from '@ant-design/icons-vue'
+  import Chatbot from '@/components/chatbot/chatbot.vue'
 </script>
 
 <template>
@@ -37,6 +38,7 @@
     <div class="content">
       <router-view />
     </div>
+    <chatbot />
   </a-layout-content>
 </template>
 
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/locales/en_US/ai.ts
similarity index 57%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/locales/en_US/ai.ts
index 7fc6f12..6a13d96 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/en_US/ai.ts
@@ -17,20 +17,17 @@
  * under the License.
  */
 
-import common from '@/locales/en_US/common.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import hosts from '@/locales/en_US/hosts.ts'
-import service from '@/locales/en_US/service.ts'
-import job from '@/locales/en_US/job.ts'
-
 export default {
-  common,
-  login,
-  user,
-  cluster,
-  hosts,
-  service,
-  job
+  empty_message: 'Message cannot be empty',
+  select_authorized_platform: 'Please select the following authorized 
platform',
+  no_authorized_platform: 'No authorized platforms',
+  or_you_can: 'Or you can',
+  authorize_new_platform: 'Authorize New Platform',
+  select_platform_to_authorize: 'Select platform to authorize',
+  authorizing_platform:
+    ' You are authorizing {0} platform, please fill in the following 
information',
+  select_thread_to_chat: 'Please select the thread to enter chat',
+  select_model: 'Please select the model you want to use',
+  create_new_thread: 'Create New Thread',
+  thread_name: 'Thread {0}'
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/common.ts 
b/bigtop-manager-ui/src/locales/en_US/common.ts
index 920312f..8b33e62 100644
--- a/bigtop-manager-ui/src/locales/en_US/common.ts
+++ b/bigtop-manager-ui/src/locales/en_US/common.ts
@@ -58,5 +58,12 @@ export default {
   notification: 'Notification',
   copy: 'Copy',
   copy_success: 'Copy success!',
-  copy_fail: 'Copy failed!'
+  copy_fail: 'Copy failed!',
+  delete_success: 'Delete success!',
+  delete_fail: 'Delete failed!',
+  delete_confirm_title: 'Delete Confirm',
+  delete_confirm_content:
+    'Delete will not be recoverable, are you sure you want to delete {0} ?',
+  no_options: 'No options',
+  loadingText_verifying: 'Verifying'
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/locales/en_US/index.ts
index 7fc6f12..b367ad1 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/en_US/index.ts
@@ -24,6 +24,7 @@ import cluster from '@/locales/en_US/cluster.ts'
 import hosts from '@/locales/en_US/hosts.ts'
 import service from '@/locales/en_US/service.ts'
 import job from '@/locales/en_US/job.ts'
+import ai from '@/locales/en_US/ai.ts'
 
 export default {
   common,
@@ -32,5 +33,6 @@ export default {
   cluster,
   hosts,
   service,
-  job
+  job,
+  ai
 }
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/locales/zh_CN/ai.ts
similarity index 59%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/locales/zh_CN/ai.ts
index 7fc6f12..551f465 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/ai.ts
@@ -17,20 +17,16 @@
  * under the License.
  */
 
-import common from '@/locales/en_US/common.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import hosts from '@/locales/en_US/hosts.ts'
-import service from '@/locales/en_US/service.ts'
-import job from '@/locales/en_US/job.ts'
-
 export default {
-  common,
-  login,
-  user,
-  cluster,
-  hosts,
-  service,
-  job
+  empty_message: '消息内容不能为空',
+  select_authorized_platform: '请选择下列已授权的平台',
+  no_authorized_platform: '暂无授权平台',
+  or_you_can: '或者你可以',
+  authorize_new_platform: '授权新平台',
+  select_platform_to_authorize: '请选择下列要授权的平台',
+  authorizing_platform: '您正在授权 {0} 平台, 请填写下列信息',
+  select_thread_to_chat: '请选择下面的线程进入聊天',
+  select_model: '请选择您要使用的模型',
+  create_new_thread: '创建新线程',
+  thread_name: '线程 {0}'
 }
diff --git a/bigtop-manager-ui/src/locales/zh_CN/common.ts 
b/bigtop-manager-ui/src/locales/zh_CN/common.ts
index 8de97e0..f9694f0 100644
--- a/bigtop-manager-ui/src/locales/zh_CN/common.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/common.ts
@@ -58,5 +58,11 @@ export default {
   notification: '通知',
   copy: '复制',
   copy_success: '复制成功',
-  copy_fail: '复制失败'
+  copy_fail: '复制失败',
+  delete_success: '删除成功',
+  delete_fail: '删除失败',
+  delete_confirm_title: '删除确认',
+  delete_confirm_content: '删除后无法恢复,确定要删除 {0} 吗?',
+  no_options: '暂无选项',
+  loadingText_verifying: '校验中'
 }
diff --git a/bigtop-manager-ui/src/locales/zh_CN/index.ts 
b/bigtop-manager-ui/src/locales/zh_CN/index.ts
index 7ebd07b..fe7d911 100644
--- a/bigtop-manager-ui/src/locales/zh_CN/index.ts
+++ b/bigtop-manager-ui/src/locales/zh_CN/index.ts
@@ -24,6 +24,7 @@ import cluster from '@/locales/zh_CN/cluster.ts'
 import hosts from '@/locales/zh_CN/hosts.ts'
 import service from '@/locales/zh_CN/service.ts'
 import job from '@/locales/zh_CN/job.ts'
+import ai from '@/locales/zh_CN/ai.ts'
 
 export default {
   common,
@@ -32,5 +33,6 @@ export default {
   cluster,
   hosts,
   service,
-  job
+  job,
+  ai
 }
diff --git a/bigtop-manager-ui/src/main.ts b/bigtop-manager-ui/src/main.ts
index 92dcea5..6539e20 100644
--- a/bigtop-manager-ui/src/main.ts
+++ b/bigtop-manager-ui/src/main.ts
@@ -21,7 +21,9 @@ import { createApp } from 'vue'
 import App from './App.vue'
 import plugins from '@/plugins'
 
-import '@/styles/default.css'
+import '@/styles/main.css'
+import '@/styles/scrollbar.scss'
+import '@/styles/marked.scss'
 import 'ant-design-vue/dist/reset.css'
 import 'virtual:svg-icons-register'
 
diff --git a/bigtop-manager-ui/src/plugins/index.ts 
b/bigtop-manager-ui/src/plugins/index.ts
index a6a8089..d802617 100644
--- a/bigtop-manager-ui/src/plugins/index.ts
+++ b/bigtop-manager-ui/src/plugins/index.ts
@@ -23,7 +23,7 @@ import pinia from '@/store'
 import i18n from '@/locales'
 import Antd, { message } from 'ant-design-vue'
 import components from '@/components/common'
-
+import VueDOMPurifyHTML from 'vue-dompurify-html'
 interface PluginOptions {
   antdMessageMaxCount: number
 }
@@ -35,6 +35,7 @@ export default {
     app.use(pinia)
     app.use(i18n)
     app.use(components)
+    app.use(VueDOMPurifyHTML) // xss defense
     message.config({ maxCount: options.antdMessageMaxCount })
   }
 }
diff --git a/bigtop-manager-ui/src/styles/function.scss 
b/bigtop-manager-ui/src/styles/common/index.scss
similarity index 93%
rename from bigtop-manager-ui/src/styles/function.scss
rename to bigtop-manager-ui/src/styles/common/index.scss
index 61f5439..2edafe0 100644
--- a/bigtop-manager-ui/src/styles/function.scss
+++ b/bigtop-manager-ui/src/styles/common/index.scss
@@ -15,4 +15,7 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */
\ No newline at end of file
+ */
+
+@import './variables.scss';
+@import './mixins.scss';
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/styles/mixins.scss 
b/bigtop-manager-ui/src/styles/common/mixins.scss
similarity index 100%
rename from bigtop-manager-ui/src/styles/mixins.scss
rename to bigtop-manager-ui/src/styles/common/mixins.scss
diff --git a/bigtop-manager-ui/src/styles/variables.scss 
b/bigtop-manager-ui/src/styles/common/variables.scss
similarity index 100%
rename from bigtop-manager-ui/src/styles/variables.scss
rename to bigtop-manager-ui/src/styles/common/variables.scss
diff --git a/bigtop-manager-ui/src/styles/default.css 
b/bigtop-manager-ui/src/styles/main.css
similarity index 97%
copy from bigtop-manager-ui/src/styles/default.css
copy to bigtop-manager-ui/src/styles/main.css
index dcd70d8..addd8b5 100644
--- a/bigtop-manager-ui/src/styles/default.css
+++ b/bigtop-manager-ui/src/styles/main.css
@@ -17,8 +17,6 @@
  * under the License.
  */
 
-@import './scrollbar.css';
-
 * {
   outline: 0;
 }
diff --git a/bigtop-manager-ui/src/styles/main.scss 
b/bigtop-manager-ui/src/styles/main.scss
deleted file mode 100644
index 0dbde25..0000000
--- a/bigtop-manager-ui/src/styles/main.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *    https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-@import '@/styles/function.scss';
-@import '@/styles/variables.scss';
-@import '@/styles/mixins.scss'
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/styles/default.css 
b/bigtop-manager-ui/src/styles/marked.scss
similarity index 56%
rename from bigtop-manager-ui/src/styles/default.css
rename to bigtop-manager-ui/src/styles/marked.scss
index dcd70d8..1d6b6cb 100644
--- a/bigtop-manager-ui/src/styles/default.css
+++ b/bigtop-manager-ui/src/styles/marked.scss
@@ -17,42 +17,48 @@
  * under the License.
  */
 
-@import './scrollbar.css';
+@import 'github-markdown-css/github-markdown.css';
+@import 'highlight.js/styles/github-dark.css';
 
-* {
-  outline: 0;
-}
+.markdown-body {
+  background-color: inherit;
+  color: #333333;
+  box-sizing: border-box;
+  font-size: 1em;
 
-html {
-  --text-color: rgba(0, 0, 0, 0.85);
-  --text-color-desc: rgba(0, 0, 0, 0.45);
-  --bg-color: #fff;
-  --hover-color: rgba(0, 0, 0, 0.05);
-  --bg-color-container: #f0f2f5;
-  --c-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
+  pre {
+    border-radius: 8px;
+  }
 }
 
-.m-0 {
-  margin: 0;
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3 {
+  color: #000000;
 }
 
-.p-0 {
-  padding: 0;
+.markdown-body a {
+  color: #007bff;
 }
 
-.hidden {
-  display: none;
+.markdown-body a:hover {
+  text-decoration: underline;
 }
 
-.ant-table-body {
-  overflow-y: auto !important;
+.markdown-body ul {
+  list-style-type: disc;
+  margin-left: 10px;
+  margin-bottom: 10px;
 }
 
-#app {
-  width: 100%;
-  box-sizing: border-box;
-  position: fixed;
-  top: 0;
-  bottom: 0;
-  left: 0;
+.markdown-body ul li {
+  margin-bottom: 10px;
 }
+
+.code-block-head {
+  background: #4c4d58;
+  display: flex;
+  justify-content: space-between;
+  padding: 6px;
+  align-items: center;
+}
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/styles/scrollbar.css 
b/bigtop-manager-ui/src/styles/scrollbar.scss
similarity index 91%
rename from bigtop-manager-ui/src/styles/scrollbar.css
rename to bigtop-manager-ui/src/styles/scrollbar.scss
index 095c5d4..4e38af8 100644
--- a/bigtop-manager-ui/src/styles/scrollbar.css
+++ b/bigtop-manager-ui/src/styles/scrollbar.scss
@@ -16,31 +16,36 @@
  * specific language governing permissions and limitations
  * under the License.
  */
- 
+
 ::-webkit-scrollbar {
   width: 6px;
   height: 6px;
 }
+
 ::scrollbar {
   width: 6px;
   height: 6px;
 }
+
 ::-webkit-scrollbar-track {
   display: none;
   -webkit-box-shadow: inset 0 0 2px #b18933;
 }
+
 ::scrollbar-track {
   display: none;
-  -webkit-box-shadow: inset 0 0 2px #98f165;
+  box-shadow: inset 0 0 2px #98f165;
   border-radius: 1px;
 }
+
 ::-webkit-scrollbar-thumb {
   border-radius: 10px;
   background: #c1c1c1;
   -webkit-box-shadow: inset 0 0 1px rgba(0, 102, 102, 0.144);
 }
+
 ::scrollbar-thumb {
   border-radius: 10px;
   background: #c1c1c1;
-  -webkit-box-shadow: inset 0 0 1px rgba(0, 102, 102, 0.144);
-}
+  box-shadow: inset 0 0 1px rgba(0, 102, 102, 0.144);
+}
\ No newline at end of file
diff --git a/bigtop-manager-ui/src/locales/en_US/index.ts 
b/bigtop-manager-ui/src/utils/render.ts
similarity index 63%
copy from bigtop-manager-ui/src/locales/en_US/index.ts
copy to bigtop-manager-ui/src/utils/render.ts
index 7fc6f12..6220d47 100644
--- a/bigtop-manager-ui/src/locales/en_US/index.ts
+++ b/bigtop-manager-ui/src/utils/render.ts
@@ -17,20 +17,19 @@
  * under the License.
  */
 
-import common from '@/locales/en_US/common.ts'
-import login from '@/locales/en_US/login'
-import user from '@/locales/en_US/user.ts'
-import cluster from '@/locales/en_US/cluster.ts'
-import hosts from '@/locales/en_US/hosts.ts'
-import service from '@/locales/en_US/service.ts'
-import job from '@/locales/en_US/job.ts'
+import { Marked } from 'marked'
+import { markedHighlight } from 'marked-highlight'
+import hljs from 'highlight.js'
 
-export default {
-  common,
-  login,
-  user,
-  cluster,
-  hosts,
-  service,
-  job
+export const parseMDByHighlight = (content: string) => {
+  const marked = new Marked(
+    markedHighlight({
+      langPrefix: 'hljs language-',
+      highlight(code, lang) {
+        const language = hljs.getLanguage(lang) ? lang : 'plaintext'
+        return hljs.highlight(code, { language }).value
+      }
+    })
+  )
+  return marked.parse(content)
 }
diff --git a/bigtop-manager-ui/vite.config.ts b/bigtop-manager-ui/vite.config.ts
index ff4ffe6..50a2b2a 100644
--- a/bigtop-manager-ui/vite.config.ts
+++ b/bigtop-manager-ui/vite.config.ts
@@ -47,7 +47,7 @@ export default defineConfig(({ mode }) => {
     css: {
       preprocessorOptions: {
         scss: {
-          additionalData: '@import "@/styles/main.scss";'
+          additionalData: '@import "@/styles/common/index.scss";'
         }
       }
     },

Reply via email to