浏览代码

fix: chat

lvkun996 1 年之前
父节点
当前提交
dd3177ae7e

+ 2 - 1
.eslintrc.js

@@ -24,6 +24,7 @@ module.exports = {
     'vue/no-unused-vars': 'off',
     'no-useless-escape': 'off',
     'no-use-before-define': 'off',
-    'vue/no-setup-props-destructure': 'off'
+    'vue/no-setup-props-destructure': 'off',
+    'brace-style': 'off'
   }
 }

+ 77 - 0
Dockerfile

@@ -0,0 +1,77 @@
+# base image
+FROM node:20-alpine3.20 AS base
+LABEL maintainer="takatost@gmail.com"
+
+# if you located in China, you can use aliyun mirror to speed up
+RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
+
+RUN apk add --no-cache tzdata
+RUN npm install -g pnpm@9.12.2
+ENV PNPM_HOME="/pnpm"
+ENV PATH="$PNPM_HOME:$PATH"
+RUN pnpm config set registry https://registry.npmmirror.com
+RUN pnpm self-update
+
+# install packages
+FROM base AS packages
+
+WORKDIR /app/web
+
+COPY package.json .
+COPY pnpm-lock.yaml .
+
+# if you located in China, you can use taobao registry to speed up
+# RUN pnpm install --frozen-lockfile --registry https://registry.npmmirror.com/
+
+RUN pnpm install --frozen-lockfile
+
+# build resources
+FROM base AS builder
+WORKDIR /app/web
+COPY --from=packages /app/web/ .
+COPY . .
+
+ENV NODE_OPTIONS="--max-old-space-size=4096"
+RUN pnpm build
+
+
+# production stage
+FROM base AS production
+
+ENV NODE_ENV=production
+ENV EDITION=SELF_HOSTED
+ENV DEPLOY_ENV=PRODUCTION
+ENV CONSOLE_API_URL=http://127.0.0.1:5001
+ENV APP_API_URL=http://127.0.0.1:5001
+ENV MARKETPLACE_API_URL=http://127.0.0.1:5001
+ENV MARKETPLACE_URL=http://127.0.0.1:5001
+ENV PORT=3000
+ENV NEXT_TELEMETRY_DISABLED=1
+ENV PM2_INSTANCES=2
+
+# set timezone
+ENV TZ=UTC
+RUN ln -s /usr/share/zoneinfo/${TZ} /etc/localtime \
+    && echo ${TZ} > /etc/timezone
+
+
+WORKDIR /app/web
+COPY --from=builder /app/web/public ./public
+COPY --from=builder /app/web/.next/standalone ./
+COPY --from=builder /app/web/.next/static ./.next/static
+
+COPY docker/entrypoint.sh ./entrypoint.sh
+
+
+# global runtime packages
+RUN pnpm add -g pm2 \
+    && mkdir /.pm2 \
+    && chown -R 1001:0 /.pm2 /app/web \
+    && chmod -R g=u /.pm2 /app/web
+
+ARG COMMIT_SHA
+ENV COMMIT_SHA=${COMMIT_SHA}
+
+USER 1001
+EXPOSE 3000
+ENTRYPOINT ["/bin/sh", "./entrypoint.sh"]

+ 3 - 29
config/proxy.ts

@@ -5,36 +5,10 @@
 
 module.exports = {
   dev: {
-    '/user': {
-      // target: 'http://120.223.238.91:8888',
-      // target: 'http://124.222.113.37:9999',
-      // target: 'http://124.222.113.37:9999',
-      target: 'http://101.126.68.200:18081',
+    '/api': {
+      target: 'http://cvfy.natapp1.cc/',
       changeOrigin: true,
-      pathRewrite: { '^/user': '' }
-    },
-    '/cvss': {
-      // target: 'http://120.223.238.91:6666',
-      // target: 'http://172.28.0.3:18082',
-      // target: 'http://124.222.113.37:8080',
-      // target: 'http://101.126.68.200:18081',
-      target: 'http://111.231.55.175:18082',
-      changeOrigin: true,
-      pathRewrite: { '^/cvss': '' }
-    },
-    '/iot': {
-      // target: 'http://120.223.238.91:8888',
-      // target: 'http://120.223.238.91:8888',
-      // target: 'http://124.222.113.37:8888',
-      target: 'http://101.126.68.200:18081',
-      changeOrigin: true,
-      pathRewrite: { '^/iot': '' }
-    },
-    '/datacenter': {
-      // target: 'http://152.136.36.115:7777',
-      target: 'hhttp://120.223.238.91',
-      changeOrigin: true,
-      pathRewrite: { '^/datacenter': '' }
+      pathRewrite: { '^/api': '' }
     }
   }
 }

+ 4 - 2
package.json

@@ -30,11 +30,13 @@
     "codemirror": "^6.0.1",
     "commander": "^11.1.0",
     "core-js": "^3.8.3",
-    "dayjs": "^1.11.7",
+    "dayjs": "^1.11.13",
     "echarts": "^5.4.2",
-    "flicker-vue-hooks": "^1.0.60",
+    "flicker-vue-hooks": "^1.0.64",
+    "highlight": "^0.2.4",
     "html2canvas": "^1.4.1",
     "inquirer": "^8.2.6",
+    "markdown-it": "^14.1.0",
     "minimist": "^1.2.8",
     "mitt": "^3.0.0",
     "normalize.css": "^8.0.1",

+ 47 - 0
src/api/chat.ts

@@ -0,0 +1,47 @@
+import request from '@/service/request'
+
+export const setDialog = (data: CHAT.DIALOG_PARAMS) => {
+  return request<string>({
+    url: '/dialog/set',
+    method: 'POST',
+    data
+  })
+}
+
+export const rmDialog = (id: string) => {
+  return request<string>({
+    url: '/dialog/rm',
+    method: 'POST',
+    data: { id }
+  })
+}
+
+export const dialoglist = () => {
+  return request<CHAT.DIALOG>({
+    url: 'dialog/list',
+    method: 'GET'
+  })
+}
+
+export const conversationList = (dialogId: string) => {
+  return request<CHAT.CONVERSATION[]>({
+    url: `conversation/list?dialog_id=${dialogId}`,
+    method: 'GET'
+  })
+}
+
+export const setConversation = (data: CHAT.CONVERSATION) => {
+  return request<boolean>({
+    url: '/conversation/set',
+    method: 'POST',
+    data
+  })
+}
+
+export const rmConversation = (data: {'dialog_id': string, 'conversation_ids': string[]}) => {
+  return request<boolean>({
+    url: 'conversation/rm',
+    method: 'POST',
+    data
+  })
+}

+ 9 - 0
src/api/user.ts

@@ -0,0 +1,9 @@
+import request from '@/service/request'
+
+export const login = (data: USER.LOGIN) => {
+  return request<string>({
+    url: '/user/login',
+    method: 'POST',
+    data
+  })
+}

二进制
src/assets/form-bg.png


二进制
src/assets/login-bg.png


二进制
src/assets/wechat.png


+ 30 - 0
src/controller/ChatController.ts

@@ -0,0 +1,30 @@
+import {
+  dialoglist, rmDialog, setDialog,
+  conversationList, setConversation, rmConversation
+} from '@/api/chat'
+
+export class ChatController {
+  static async setDialog (data: CHAT.DIALOG_PARAMS) {
+    return await setDialog(data)
+  }
+
+  static async rmDialog (id: string) {
+    return await rmDialog(id)
+  }
+
+  static async dialoglist () {
+    return await dialoglist()
+  }
+
+  static async conversationList (dialogId: string) {
+    return await conversationList(dialogId)
+  }
+
+  static async setConversation (data: CHAT.CONVERSATION) {
+    return await setConversation(data)
+  }
+
+  static async rmConversation (data: {'dialog_id': string, 'conversation_ids': string[]}) {
+    return await rmConversation(data)
+  }
+}

+ 4 - 2
src/controller/UserController.ts

@@ -1,5 +1,7 @@
-export class UserController {
-  static async login (data: any) {
+import { login } from '@/api/user'
 
+export class UserController {
+  static async login (data: USER.LOGIN) {
+    return login(data)
   }
 }

+ 1 - 0
src/controller/index.ts

@@ -1,2 +1,3 @@
 export { DemoController } from './demoController'
 export { UserController } from './UserController'
+export { ChatController } from './ChatController'

+ 1 - 12
src/layout/components/Sidebar/index.vue

@@ -44,9 +44,6 @@
     collapsible
     v-model:collapsed="collapsed"
   >
-    <div class="logo" >
-      <!-- <img :src="logoPng" alt="" v-if="logoPng"> -->
-    </div>
     <a-menu
       v-model:selectedKeys="selectedKeys"
       :openKeys="openKeys"
@@ -55,7 +52,7 @@
     >
       <sidebar-item
         :item="route"
-        v-for="route in sidebarRoute"
+        v-for="route in routes[0].children"
         :key="route.path"
         :base-path="route.path"
       />
@@ -88,8 +85,6 @@ const router = useRouter()
 
 const collapsed = ref<boolean>(true)
 
-const logoPng = computed(() => collapsed.value ? AppConfig.logoContract : AppConfig.logoExpand)
-
 const sidebarRoute = ref<any>()
 
 const selectedKeys = ref<string[]>()
@@ -98,8 +93,6 @@ const openKeys = ref<string[]>()
 
 const drawerVisible = ref<boolean>(false)
 
-const compatibleRoutes = !useIsMicro() ? router.getRoutes().filter(item => item.path !== '/cloudlink') : router.getRoutes()
-
 watch(
   () => route.path,
   () => {
@@ -114,10 +107,6 @@ watch(
 
 const openDrawer = () => drawerVisible.value = true
 
-onMounted(() => {
-  console.log('我渲染了嗎')
-})
-
 </script>
 
 <style lang="less" scoped >

+ 5 - 13
src/layout/layout.vue

@@ -1,15 +1,11 @@
 <template>
-  <RouterView v-if="isMicro" ></RouterView>
     <a-layout class="a-layout">
       <Navbar />
       <a-layout >
-        <SiderBar />
+        <!-- <SiderBar /> -->
         <span style="width: 100%;" >
           <div class="content" id="content"  >
             <div class="router-view" >
-              <div style="margin-bottom: 21px;font-size: 16px;"  >
-                <breadcrumb />
-              </div>
               <RouterView ></RouterView>
             </div>
           </div>
@@ -21,10 +17,6 @@
 <script  lang="ts" setup >
 import Navbar from './navbar.vue'
 import SiderBar from './components/Sidebar/index.vue'
-import { useIsMicro } from '@/hooks/effect'
-import breadcrumb from './breadcrumb'
-
-const isMicro = useIsMicro()
 
 </script>
 
@@ -46,14 +38,14 @@ const isMicro = useIsMicro()
     .router-view {
       width: 100%;
       height: 100%;
-      padding: 0 24px 24px;
-      padding-bottom: 160px;
-      padding-top: 24px;
+      // padding: 0 24px 24px;
+      // padding-bottom: 160px;
+      // padding-top: 24px;
       box-sizing: border-box;
       overflow: hidden;
       overflow-y: scroll;
       font-family: 'MyCustomFont', cursive !important;
-      background: url('@/assets/bg.png') no-repeat top left;
+      // background: url('@/assets/bg.png') no-repeat top left;
       background-size: 100% 100%;
     }
   }

+ 60 - 22
src/layout/navbar.vue

@@ -1,27 +1,28 @@
 <template>
-   <a-layout-header class="header-mobile" v-if="isMobile" :style="{backgroundColor: headerBgColor}">
-      <a-row>
-        <a-col :span="6" >
-          <search />
-        </a-col>
-        <a-col :span="6" class="df-center">
-          <a-button type="text" @click="changeTheme" > <IconTsx :name="iconName" /></a-button>
-        </a-col>
-        <a-col :span="6" class="df-center"  >
-          <user />
-        </a-col>
-      </a-row>
-  </a-layout-header>
-  <a-layout-header class="header-pc" v-else :style="{backgroundColor: headerBgColor, height: '54px'}">
+  <a-layout-header class="header-pc" :style="{backgroundColor: headerBgColor, height: '54px'}">
     <a-row style="width: 100%;height: 54px;" justify="start" >
-        <a-col :span="8" >
-          <div class="app-title" >
-            <div class="title" >{{AppConfig.title}}</div>
-            <div class="line" ></div>
-            <div class="subtitle" >{{AppConfig.subtitle}}</div>
-          </div>
-        </a-col>
-        <a-col :span="6" style="height: 54px;" >
+        <a-col :span="15" >
+          <a-row>
+            <a-col span="4" >
+              <div class="app-title" >
+                <div class="title" >{{AppConfig.title}}</div>
+                <div class="line" ></div>
+                <div class="subtitle" >{{AppConfig.subtitle}}</div>
+              </div>
+            </a-col>
+            <a-col span="18">
+              <div class="jlc-meun" >
+                <div
+                  v-for="route in routes[0].children.filter(item => !item.meta.hidden)"
+                  :key="route.key"
+                  @click="pushPage(route)"
+                  :class="['jlc-meun-item', selectedKeys === route.key ? 'jlc-meun-item-active' : '']"
+                >
+                  {{route.name}}
+                </div>
+              </div>
+            </a-col>
+          </a-row>
         </a-col>
         <a-col :span="8" style="height: 54px;" >
           <a-row :gutter="[8, 8]" justify="end"  style="height: 54px;" >
@@ -61,6 +62,7 @@ import { DesktopOutlined } from '@ant-design/icons-vue'
 import { message } from 'ant-design-vue'
 import AppConfig from 'AppConfig'
 import { useIsMicro } from '@/hooks/effect'
+import { routes } from '@/router/index'
 
 const route = useRoute()
 
@@ -101,6 +103,11 @@ const addDeskToApp = () => {
   }
 }
 
+const pushPage = (route: any) => {
+  router.push({
+    path: route.path
+  })
+}
 </script>
 
 <style lang="less" scoped >
@@ -145,4 +152,35 @@ const addDeskToApp = () => {
   align-items: center;
 }
 
+.jlc-meun {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: flex-start;
+      color: #666666;
+      .jlc-meun-item {
+        padding: 9px 26px;
+        height: 42px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        margin-right: 16px;
+        box-sizing: border-box;
+        cursor: pointer;
+        border-radius: 30px 30px 30px 30px;
+      }
+      .jlc-meun-item-active {
+        background: #2468F2;
+        color: #fff;
+      }
+    }
+    .jlc-meun-item:hover {
+      background-color: #f3f4f7;
+      color: #000;
+    }
+    .jlc-meun-item-active:hover {
+        background: #2468F2;
+        color: #fff;
+    }
 </style>

+ 104 - 0
src/pages/chat/components/chat-history.vue

@@ -0,0 +1,104 @@
+<template>
+ <div class="conversation-list">
+      <div class="list-header">
+        <h3>会话列表</h3>
+        <button class="new-chat-btn" @click="createNewChat">
+          <span>新建会话</span>
+        </button>
+      </div>
+      <div class="conversations">
+        <div
+          v-for="(chat, index) in chatList"
+          :key="index"
+          class="conversation-item"
+          :class="{ active: currentChatId === chat.id }"
+          @click="switchChat(chat.id)"
+        >
+          <div class="chat-title">{{ chat.title }}</div>
+          <div class="chat-time">{{ formatTime(chat.lastTime) }}</div>
+        </div>
+      </div>
+    </div>
+</template>
+<script lang='ts'  setup >
+import {} from 'vue'
+
+interface IProps {
+  chatHistory: any[]
+}
+
+const props = defafineProps<IProps>()
+
+</script>
+<style lang='less' scoped >
+.conversation-list {
+  width: 260px;
+  border-right: 1px solid #e5e7eb;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f5f5; /* 添加灰色背景 */
+
+  .list-header {
+    padding: 16px;
+    border-bottom: 1px solid #e5e7eb;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+    background-color: #f5f5f5; /* 保持标题区域也是灰色背景 */
+
+    h3 {
+      margin: 0;
+      font-size: 16px;
+    }
+
+    .new-chat-btn {
+      padding: 8px 12px;
+      background-color: #3b82f6;
+      color: white;
+      border: none;
+      border-radius: 4px;
+      cursor: pointer;
+      font-size: 14px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      &:hover {
+        background-color: #2563eb;
+      }
+    }
+  }
+
+  .conversations {
+    flex: 1;
+    overflow-y: auto;
+
+    .conversation-item {
+      padding: 12px 16px;
+      cursor: pointer;
+      border-bottom: 1px solid #e5e7eb;
+
+      &:hover {
+        background-color: #eaeaea;
+      }
+
+      &.active {
+        background-color: #e0e0e0;
+      }
+
+      .chat-title {
+        font-size: 14px;
+        margin-bottom: 4px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .chat-time {
+        font-size: 12px;
+        color: #6b7280;
+      }
+    }
+  }
+}
+</style>

+ 169 - 0
src/pages/chat/components/chat-input.vue

@@ -0,0 +1,169 @@
+<template>
+   <div class="input-area">
+        <div class="input-wrapper">
+          <textarea
+            v-model="inputMessage"
+            placeholder="输入消息..."
+            @keydown.enter.prevent="sendMessage"
+          ></textarea>
+          <div class="input-toolbar">
+            <div class="toolbar-left">
+              <button class="toolbar-button">
+                <span class="icon">📊</span>
+                模型选择
+              </button>
+              <button class="toolbar-button">
+                <span class="icon">🖼️</span>
+                上传图片
+              </button>
+              <button class="toolbar-button">
+                <span class="icon">📎</span>
+                上传文件
+              </button>
+            </div>
+            <button class="send-button" @click="sendMessage">发送</button>
+          </div>
+        </div>
+     </div>
+</template>
+<script lang='ts'  setup >
+import { ref } from 'vue'
+const inputMessage = ref('')
+
+// 发送消息
+const sendMessage = () => {
+  if (!inputMessage.value.trim()) return
+
+  const userMessage: Message = {
+    role: 'user',
+    content: inputMessage.value
+  }
+
+  // 添加用户消息
+  messages.value.push(userMessage)
+
+  // 更新当前会话
+  const currentChat = chatList.value.find(c => c.id === currentChatId.value)
+  if (currentChat) {
+    currentChat.messages.push(userMessage)
+    currentChat.lastTime = new Date()
+
+    // 如果是第一条消息,更新会话标题
+    if (currentChat.messages.length === 1) {
+      currentChat.title = inputMessage.value.slice(0, 20) + (inputMessage.value.length > 20 ? '...' : '')
+    }
+  }
+
+  // 这里添加发送消息到后端的逻辑
+  // TODO: 调用后端 API
+
+  // 模拟 AI 回复
+  setTimeout(() => {
+    const aiMessage: Message = {
+      role: 'assistant',
+      content: '这是一个模拟的 AI 回复...'
+    }
+
+    messages.value.push(aiMessage)
+
+    // 更新当前会话
+    if (currentChat) {
+      currentChat.messages.push(aiMessage)
+      currentChat.lastTime = new Date()
+    }
+  }, 1000)
+
+  inputMessage.value = ''
+}
+</script>
+<style lang='less' scoped >
+.input-area {
+  width: 768px;
+  border-top: none; /* 移除上边框 */
+  position: absolute; /* 添加相对定位 */
+  bottom:98px; /* 固定在底部 */
+  left: 50%; /* 左对齐 */
+  transform: translateX(-50%); /* 水平居中 */
+  .input-wrapper {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    background-color: #fff;
+    border-radius: 12px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+    overflow: hidden;
+    max-width: 800px; /* 限制最大宽度 */
+    margin: 0 auto; /* 居中显示 */
+
+    textarea {
+      width: 100%;
+      min-height: 50px; /* 减小高度 */
+      padding: 12px 16px; /* 减小内边距 */
+      padding-bottom: 45px; /* 为底部工具栏留出空间 */
+      border: none;
+      background-color: transparent;
+      resize: none;
+      outline: none;
+      font-size: 14px;
+    }
+
+    .input-toolbar {
+      position: absolute;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      display: flex;
+      align-items: center;
+      padding: 8px 16px;
+      background-color: #f9f9f9;
+      border-top: none; /* 移除工具栏上方的分割线 */
+
+      /* 其他样式保持不变 */
+
+      .toolbar-left {
+        display: flex;
+        gap: 12px;
+        flex: 1;
+      }
+
+      .toolbar-button {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        padding: 6px 10px;
+        border-radius: 6px;
+        background: transparent;
+        border: none;
+        cursor: pointer;
+        color: #666;
+        font-size: 14px;
+        transition: all 0.2s;
+
+        &:hover {
+          background-color: #eaeaea;
+          color: #333;
+        }
+
+        .icon {
+          margin-right: 4px;
+          font-size: 16px;
+        }
+      }
+
+      .send-button {
+        padding: 6px 16px;
+        background-color: #3b82f6;
+        color: white;
+        border: none;
+        border-radius: 6px;
+        cursor: pointer;
+        transition: background-color 0.2s;
+
+        &:hover {
+          background-color: #2563eb;
+        }
+      }
+    }
+  }
+}
+</style>

+ 169 - 0
src/pages/chat/components/chat-messages.vue

@@ -0,0 +1,169 @@
+<template>
+  <div class="chat-container">
+    <div class="chat-list" >
+      <div class="messages">
+        <div v-for="(msg, index) in messages" :key="index" :class="['message', msg.role]">
+
+            <div class="content">{{msg.content}}</div>
+            <div class="message-bar" >
+              <a-space>
+                <a-tooltip placement="bottom">
+                  <template #title>
+                    <span>复制</span>
+                  </template>
+                  <CopyOutlined style="font-size: 14px" />
+                </a-tooltip>
+                <a-tooltip placement="bottom">
+                  <template #title>
+                    <span>重新生成</span>
+                  </template>
+                  <ReloadOutlined style="font-size: 14px"/>
+                </a-tooltip>
+                <a-tooltip placement="bottom">
+                  <template #title>
+                    <span>播放</span>
+                  </template>
+                  <SoundOutlined style="font-size: 14px" />
+                </a-tooltip>
+              </a-space>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" >
+import { ref } from 'vue'
+import MarkdownIt from 'markdown-it'
+import hljs from 'highlight.js'
+import 'highlight.js/styles/github.css'
+import { CopyOutlined, SoundOutlined, ReloadOutlined } from '@ant-design/icons-vue'
+
+const md = new MarkdownIt({
+  highlight: function (str, lang) {
+    if (lang && hljs.getLanguage(lang)) {
+      return `<pre><code class="hljs">${hljs.highlight(str, { language: lang }).value}</code></pre>`
+    }
+    return `<pre><code>${str}</code></pre>`
+  }
+})
+
+const messages = ref([
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'user', content: '你什么都不能帮助到我!!!!' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' },
+  { role: 'ai', content: '你好!有什么可以帮助你的吗?' }
+])
+
+const input = ref('')
+
+const renderMarkdown = (text) => md.render(text)
+
+const sendMessage = () => {
+  if (!input.value.trim()) return
+  messages.value.push({ role: 'user', content: input.value })
+  // 模拟 AI 回复
+  setTimeout(() => {
+    messages.value.push({ role: 'ai', content: "你刚才说的是:\n```javascript\nconsole.log('Hello, world!');\n```" })
+  }, 1000)
+  input.value = ''
+}
+</script>
+
+<style scoped lang="less" >
+.chat-container {
+  width: 100%;
+  height: 100%;
+  margin: auto;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  background-color: #fcfafa;
+  .chat-list {
+    width: 100%;
+    padding-top: 60px;
+    overflow-y: auto;
+    max-height: 100vh;
+    background: #f9f9f9;
+
+  padding-bottom: 360px;
+    .messages {
+      width: 768px;
+      padding: 10px;
+      margin: 0px auto;
+      .message {
+        display: flex;
+        flex-direction: column;
+        // align-items: flex-start;
+        margin-bottom: 10px;
+      }
+      .ai {
+        display: flex;
+        justify-content: flex-start;
+          .content {
+            padding: 10px  0px ;
+            max-width: 80%;
+            white-space: pre-wrap;
+            font-size: 16px;
+            text-align: left;
+            // background-color: #fff;
+          }
+        }
+      .user {
+        display: flex;
+        justify-content: flex-end;
+        text-align: right;
+        .content {
+          width: max-content;
+          padding: 12px 24px;
+          border-radius: 24px;
+          margin-left: auto;
+          max-width: 80%;
+          white-space: pre-wrap;
+          font-size: 16px;
+          box-sizing: border-box;
+          background-color: #fff;
+        }
+      }
+
+      .message-bar {
+        // display: flex;
+        // justify-content: flex-end;
+        margin-top: 4px;
+      }
+    }
+  }
+}
+
+</style>

+ 204 - 0
src/pages/chat/index.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="chat-page">
+    <!-- 左侧会话列表 -->
+    <div class="conversation-list">
+      <div class="list-header">
+        <h3>会话列表</h3>
+        <button class="new-chat-btn" @click="createNewChat">
+          <span>新建聊天</span>
+        </button>
+      </div>
+      <div class="conversations">
+        <div
+          v-for="(chat, index) in chatList"
+          :key="chat.conversation_id"
+          class="conversation-item"
+          :class="{ active: currentChatId === chat.conversation_id }"
+          @click="switchChat(chat.conversation_id)"
+        >
+          <div class="chat-title">{{ chat.name }}</div>
+          <div class="chat-time">{{ dayjs(chat.create_time).format('YYYY-MM-DD HH:mm:ss') }}</div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 聊天区域 -->
+    <div class="chat-container" >
+      <chat-messages />
+      <!-- 输入区域 -->
+      <chat-input />
+    </div>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted } from 'vue'
+import ChatMessages from './components/chat-messages.vue' // 导入 ChatRecord 组件
+import ChatInput from './components/chat-input.vue' // 导入 ChatInput 组件
+import { ChatController } from '@/controller'
+import { useRoute } from 'vue-router'
+import dayjs from 'dayjs'
+import { useId } from 'flicker-vue-hooks'
+
+interface Message {
+  role: 'user' | 'assistant'
+  content: string
+}
+
+interface Chat {
+  id: string
+  title: string
+  lastTime: Date
+  messages: Message[]
+}
+
+const route = useRoute()
+
+const dialogId = route.query.dialogId as string
+
+// 聊天消息
+const messages = ref<Message[]>([])
+const inputMessage = ref('')
+const userAvatar = '/user-avatar.png' // 替换为实际的用户头像路径
+const aiAvatar = '/ai-avatar.png' // 替换为实际的 AI 头像路径
+
+// 会话列表
+const chatList = ref<CHAT.CONVERSATION[]>([])
+const currentChatId = ref('1')
+
+// 格式化时间
+const formatTime = (date: Date) => {
+  const now = new Date()
+  const diff = now.getTime() - date.getTime()
+
+  // 如果是今天,显示时间
+  if (diff < 24 * 60 * 60 * 1000) {
+    return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
+  } else if (diff < 7 * 24 * 60 * 60 * 1000) {
+    const days = ['日', '一', '二', '三', '四', '五', '六']
+    return `星期${days[date.getDay()]}`
+  }
+  // 否则显示日期
+  else {
+    return date.toLocaleDateString('zh-CN')
+  }
+}
+
+// 切换会话
+const switchChat = (chatId: string) => {
+  currentChatId.value = chatId
+  const chat = chatList.value.find(c => c.id === chatId)
+  if (chat) {
+    messages.value = [...chat.messages]
+  }
+}
+
+// 创建新会话
+const createNewChat = () => {
+  const conversationId = useId()
+  chatList.value.unshift({
+    conversation_id: conversationId,
+    name: '新会话',
+    create_time: new Date().getTime(),
+    is_new: true
+  })
+  switchChat(conversationId)
+}
+
+// 请求会话列表
+const getConversationList = async () => {
+  const { data } = await ChatController.conversationList(dialogId)
+  chatList.value = data
+}
+
+onMounted(() => {
+  getConversationList()
+})
+
+</script>
+
+<style lang="less" scoped>
+.chat-page {
+  display: flex;
+  height: 100vh;
+  background-color: #fcfafa;
+  position: relative;
+  .chat-container {
+    width: 100%;
+    position: relative;
+  }
+}
+
+.conversation-list {
+  width: 260px;
+  border-right: 1px solid #e5e7eb;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f5f5; /* 添加灰色背景 */
+
+  .list-header {
+    padding: 16px;
+    border-bottom: 1px solid #e5e7eb;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+    background-color: #f5f5f5; /* 保持标题区域也是灰色背景 */
+
+    h3 {
+      margin: 0;
+      font-size: 16px;
+    }
+
+    .new-chat-btn {
+      padding: 8px 12px;
+      background-color: #3b82f6;
+      color: white;
+      border: none;
+      border-radius: 4px;
+      cursor: pointer;
+      font-size: 14px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      &:hover {
+        background-color: #2563eb;
+      }
+    }
+  }
+
+  .conversations {
+    flex: 1;
+    overflow-y: auto;
+
+    .conversation-item {
+      padding: 12px 16px;
+      cursor: pointer;
+      border-bottom: 1px solid #e5e7eb;
+
+      &:hover {
+        background-color: #eaeaea;
+      }
+
+      &.active {
+        background-color: #e0e0e0;
+      }
+
+      .chat-title {
+        font-size: 14px;
+        margin-bottom: 4px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .chat-time {
+        font-size: 12px;
+        color: #6b7280;
+      }
+    }
+  }
+}
+
+</style>

+ 60 - 0
src/pages/chat/list.vue

@@ -0,0 +1,60 @@
+<template>
+<a-card>
+  <table-pro
+    :columns="column"
+    :service="ChatController.dialoglist"
+  >
+    <template #render="{column, record}" >
+      <template v-if="column.dataIndex === 'action'" >
+        <a-space>
+          <a @click="pushChatPage(record)" >进入聊天</a>
+          <a>编辑</a>
+          <a>删除</a>
+        </a-space>
+      </template>
+    </template>
+  </table-pro>
+</a-card>
+</template>
+<script lang='ts'  setup >
+import { ChatController } from '@/controller/index'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+
+const column = [
+  {
+    title: '名称',
+    dataIndex: 'name'
+  },
+  {
+    title: '描述',
+    dataIndex: 'description'
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'create_date'
+  },
+  {
+    title: '更新时间',
+    dataIndex: 'update_date'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action'
+  }
+]
+
+const pushChatPage = (record: CHAT.DIALOG) => {
+  router.push({
+    name: 'chat',
+    query: {
+      dialogId: record.id
+    }
+  })
+}
+
+</script>
+<style lang='less' scoped >
+
+</style>

+ 199 - 0
src/pages/chatv2/index.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="chat-page">
+    <!-- 左侧会话列表 -->
+    <div class="conversation-list">
+      <div class="list-header">
+        <h3>会话列表</h3>
+        <button class="new-chat-btn" @click="createNewChat">
+          <span>新建会话</span>
+        </button>
+      </div>
+      <div class="conversations">
+        <div
+          v-for="(chat, index) in chatList"
+          :key="index"
+          class="conversation-item"
+          :class="{ active: currentChatId === chat.id }"
+          @click="switchChat(chat.id)"
+        >
+          <div class="chat-title">{{ chat.title }}</div>
+          <div class="chat-time">{{ formatTime(chat.lastTime) }}</div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 聊天区域 -->
+    <div class="chat-container" >
+      <chat-messages />
+      <!-- 输入区域 -->
+      <chat-input />
+    </div>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import ChatMessages from './components/chat-messages.vue' // 导入 ChatRecord 组件
+import ChatInput from './components/chat-input.vue' // 导入 ChatInput 组件
+
+interface Message {
+  role: 'user' | 'assistant'
+  content: string
+}
+
+interface Chat {
+  id: string
+  title: string
+  lastTime: Date
+  messages: Message[]
+}
+
+// 聊天消息
+const messages = ref<Message[]>([])
+const inputMessage = ref('')
+const userAvatar = '/user-avatar.png' // 替换为实际的用户头像路径
+const aiAvatar = '/ai-avatar.png' // 替换为实际的 AI 头像路径
+
+// 会话列表
+const chatList = ref<Chat[]>([
+  {
+    id: '1',
+    title: '关于Vue3的讨论',
+    lastTime: new Date(),
+    messages: []
+  },
+  {
+    id: '2',
+    title: 'TypeScript学习',
+    lastTime: new Date(Date.now() - 24 * 60 * 60 * 1000),
+    messages: []
+  }
+])
+const currentChatId = ref('1')
+
+// 格式化时间
+const formatTime = (date: Date) => {
+  const now = new Date()
+  const diff = now.getTime() - date.getTime()
+
+  // 如果是今天,显示时间
+  if (diff < 24 * 60 * 60 * 1000) {
+    return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
+  } else if (diff < 7 * 24 * 60 * 60 * 1000) {
+    const days = ['日', '一', '二', '三', '四', '五', '六']
+    return `星期${days[date.getDay()]}`
+  }
+  // 否则显示日期
+  else {
+    return date.toLocaleDateString('zh-CN')
+  }
+}
+
+// 切换会话
+const switchChat = (chatId: string) => {
+  currentChatId.value = chatId
+  const chat = chatList.value.find(c => c.id === chatId)
+  if (chat) {
+    messages.value = [...chat.messages]
+  }
+}
+
+// 创建新会话
+const createNewChat = () => {
+  const newId = Date.now().toString()
+  chatList.value.unshift({
+    id: newId,
+    title: `新会话 ${chatList.value.length + 1}`,
+    lastTime: new Date(),
+    messages: []
+  })
+  switchChat(newId)
+}
+
+</script>
+
+<style lang="less" scoped>
+.chat-page {
+  display: flex;
+  height: 100vh;
+  background-color: #fcfafa;
+  position: relative;
+  .chat-container {
+    width: 100%;
+    position: relative;
+  }
+}
+
+.conversation-list {
+  width: 260px;
+  border-right: 1px solid #e5e7eb;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f5f5; /* 添加灰色背景 */
+
+  .list-header {
+    padding: 16px;
+    border-bottom: 1px solid #e5e7eb;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+    background-color: #f5f5f5; /* 保持标题区域也是灰色背景 */
+
+    h3 {
+      margin: 0;
+      font-size: 16px;
+    }
+
+    .new-chat-btn {
+      padding: 8px 12px;
+      background-color: #3b82f6;
+      color: white;
+      border: none;
+      border-radius: 4px;
+      cursor: pointer;
+      font-size: 14px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      &:hover {
+        background-color: #2563eb;
+      }
+    }
+  }
+
+  .conversations {
+    flex: 1;
+    overflow-y: auto;
+
+    .conversation-item {
+      padding: 12px 16px;
+      cursor: pointer;
+      border-bottom: 1px solid #e5e7eb;
+
+      &:hover {
+        background-color: #eaeaea;
+      }
+
+      &.active {
+        background-color: #e0e0e0;
+      }
+
+      .chat-title {
+        font-size: 14px;
+        margin-bottom: 4px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+
+      .chat-time {
+        font-size: 12px;
+        color: #6b7280;
+      }
+    }
+  }
+}
+
+</style>

+ 9 - 0
src/pages/flow/index.vue

@@ -0,0 +1,9 @@
+<template>
+flow
+</template>
+<script lang='ts'  setup >
+
+</script>
+<style lang='less' scoped >
+
+</style>

+ 9 - 0
src/pages/knowledge/index.vue

@@ -0,0 +1,9 @@
+<template>
+knowledge
+</template>
+<script lang='ts'  setup >
+
+</script>
+<style lang='less' scoped >
+
+</style>

+ 124 - 62
src/pages/login/index.vue

@@ -1,71 +1,67 @@
 <template>
-  <a-row
-    style="width: 100vw; height: 100vh, overflow: hidden"
-    justify="center"
+  <a-config-provider
   >
-    <a-col :span="16" style="height: 100vh;" >
-      <img
-        style="width: 100%; height: 100%; object-fit: cover"
-        :src='staticImg.loginBg'
-        alt=""
-      />
-    </a-col>
-    <a-col
-      :span="8"
-      style="display: flex; justify-content: center;align-items: center"
+<div class="login-container">
+    <div class="login-form-container">
+      <div class="title" >
+        登录
+      </div>
+      <a-form
+        name="basic"
+        autocomplete="off"
+        @keyup.enter="login"
       >
-      <a-row :gutter="[8, 8]" >
-        <a-col class="app-name" :span="24" style="display: flex;justify-content: center;">
-          蛟龙云联
-        </a-col>
-        <a-col :span="24" >
-          <a-form
-            name="basic"
-            :label-col="{ span: 8 }"
-            :wrapper-col="{ span: 16 }"
-            autocomplete="off"
-          >
-            <a-form-item
-              label="账号"
-              name="userAccount"
-              :rules="[{ required: true, message: '请填写账号' }]"
-            >
-              <a-input style="width: 328px;height: 42px;"  v-model:value="state.userAccount" />
-            </a-form-item>
+        <a-form-item
+          ref="username"
+          name="username"
+          :rules="[{ required: true, message: '请填写邮箱' }]"
+        >
+          <a-input style="width: 368px;height: 40px;"  v-model:value="state.email" placeholder="请填写邮箱">
+            <template #prefix>
+                <user-outlined />
+            </template>
+          </a-input>
+        </a-form-item>
+        <a-form-item
+          ref="password"
+          name="password"
+          :rules="[{ required: false, message: '请输入密码' }]"
+        >
+          <div class="password-form-item" >
+            <a-input-password  style="width: 368px;height: 40px;margin-bottom: 12px;"   v-model:value="state.password" placeholder="请输入密码" >
+              <template #prefix>
+                  <LockOutlined />
+              </template>
+            </a-input-password>
+            <a-checkbox :checked="true">自动登录</a-checkbox>
+          </div>
+        </a-form-item>
+        <a-form-item>
+
+        </a-form-item>
+      </a-form>
+
+      <div class="opra-button" >
+        <a-button  class="login-button" style="width: 368px;  height: 40px;" type="primary" @click="login"   >登录</a-button>
+        <a-button :bordered="false" style="width: 368px;  height: 40px;"   class="wechat-login-button" @click="wechatLogin" >
+          <img style="width: 24px;height: 24px;" class="icon" :src="require('@/assets/wechat.png')" alt="">
+          <span>微信登录</span>
+        </a-button>
+      </div>
+    </div>
+</div>
+</a-config-provider>
 
-            <a-form-item
-              label="密码"
-              name="password"
-              :rules="[{ required: true, message: '请输入密码' }]"
-            >
-              <a-input-password  style="width: 328px;height: 42px;"   v-model:value="state.password" />
-            </a-form-item>
-          </a-form>
-        </a-col>
-        <a-col :span="24" style="display: flex;justify-content: center;">
-          <a-button @click="login" style="width: 200px;" type="primary" >登录</a-button>
-        </a-col>
-      </a-row>
-    </a-col>
-  </a-row>
 </template>
 <script lang='ts' setup >
-import { useUserStore } from '@/store/index'
+import { useUserStore } from '@/store'
+import { message } from 'ant-design-vue'
 import { onUnmounted, reactive } from 'vue'
-import { useStaticImg } from '@/utils/static'
-
-// const { redirct_url } = useRoute().query
 
 const userStore = useUserStore()
 
-const staticImg = useStaticImg()
-
-window.addEventListener('keypress', event => {
-  if (event.key === 'Enter') login()
-})
-
 const state = reactive({
-  userAccount: '',
+  email: '',
   password: ''
 })
 
@@ -73,15 +69,81 @@ const login = () => {
   userStore.login(state)
 }
 
+const wechatLogin = () => message.info('微信登录开通中...')
+
 onUnmounted(() => {
   window.removeEventListener('keypress', () => {})
 })
-
 </script>
 <style lang='less' scoped >
-.app-name {
-  font: 40px 'Italiana', sans-serif;
-  text-transform: lowercase;
-  text-align: center;
+.login-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100vh;
+  width: 100vw;
+  background: url(@/assets/login-bg.png);
+  background-size: 100% 100%;
+  position: relative;
+}
+
+.login-form-container {
+  width: 480px;
+  height: 560px;
+  background: url(@/assets/form-bg.png);
+  background-size: 100% 100%;
+  position: absolute;
+  top: 50%;
+  right: 180px;
+  transform: translateY(-50%);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  box-sizing: border-box;
+  .title {
+    width: 48px;
+    height: 36px;
+    font-family: Source Han Sans CN, Source Han Sans CN;
+    font-weight: 500;
+    font-size: 24px;
+    color: #2468F2;
+    border-bottom: 1px solid #2468F2;
+    line-height: 28px;
+    text-align: center;
+    font-style: normal;
+    text-transform: none;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    top: 64px;
+  }
+  .password-form-item {
+    display: flex;
+    flex-direction: column;
+  }
+  .opra-button {
+    display: flex;
+    flex-direction: column;
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+    bottom: 89px;
+    .login-button {
+      margin-top: 48px;
+      margin-bottom: 24px
+    }
+    .wechat-login-button {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border: none;
+      .icon {
+        width: 24px;
+        height: 24px;
+        display: block;
+        margin-right: 12px;
+      }
+    }
+  }
 }
 </style>

+ 9 - 0
src/pages/search/index.vue

@@ -0,0 +1,9 @@
+<template>
+search
+</template>
+<script lang='ts'  setup >
+
+</script>
+<style lang='less' scoped >
+
+</style>

+ 59 - 30
src/router/index.ts

@@ -1,34 +1,74 @@
 import { useIsMicro } from '@/hooks/effect'
 import { createRouter, createWebHistory, RouterView } from 'vue-router'
 
-const demo = {
-  path: '/demo',
-  name: 'demo',
+const knowledge = {
+  path: '/knowledge',
+  name: '知识库',
   meta: {
-    title: 'demo'
+    title: 'knowledge'
   },
-  component: () => import('@/layout/layout.vue'),
-  redirect: '/demo',
-  children: [
-    {
-      path: '/demo',
-      name: 'demo',
-      icon: 'demo',
-      component: () => import('@/pages/demo.vue')
-    }
-  ]
+  component: () => import('@/pages/knowledge/index.vue')
+}
+
+const chatList = {
+  path: '/chat/ist',
+  name: '聊天',
+  meta: {
+    title: 'chat'
+  },
+  component: () => import('@/pages/chat/list.vue')
+}
+
+const chat = {
+  path: '/chat',
+  name: 'chat',
+  meta: {
+    hidden: true,
+    title: 'chat'
+  },
+  component: () => import('@/pages/chat/index.vue')
+}
+
+const search = {
+  path: '/search',
+  name: '搜索',
+  meta: {
+    title: 'search'
+  },
+  component: () => import('@/pages/search/index.vue')
 }
 
-const login = {
+const flow = {
+  path: '/flow',
+  name: '工作流',
+  meta: {
+    title: 'flow'
+  },
+  component: () => import('@/pages/flow/index.vue')
+}
+
+const logo = {
   path: '/login',
   name: '登录',
   meta: {
-    title: '登录'
+    hidden: true,
+    title: 'flow'
   },
   component: () => import('@/pages/login/index.vue')
 }
 
-const _routes = [demo] as any
+const APP = {
+  path: '/',
+  name: 'app',
+  meta: {
+    title: 'app'
+  },
+  component: () => import('@/layout/layout.vue'),
+  redirect: '/knowledge',
+  children: [knowledge, chat, search, chatList, flow]
+}
+
+const _routes = [APP] as any
 
 if (_routes[0].link) {
   window.open(_routes[0].path)
@@ -43,23 +83,12 @@ const redirectRoutes = {
   redirect: _routes[0].redirect
 }
 
-export const routes: Array<ROUTER.RoutesProps> = [redirectRoutes, ..._routes, login].map(item => {
-  const _item = item.link
-    ? {
-        ...item,
-        path: '/' + item.path
-      }
-    : item
-  return _item
-})
-
-const microRouter = routes
-
+export const routes: Array<ROUTER.RoutesProps> = _routes
 const baseUri = './'
 
 const router = createRouter({
   history: createWebHistory(baseUri),
-  routes: microRouter
+  routes
 })
 
 export default router

+ 2 - 2
src/service/request.ts

@@ -5,7 +5,7 @@ import { useModule } from '@/hooks'
 import { useUserStore } from '@/store'
 
 export const instance = axios.create({
-  baseURL: './',
+  baseURL: '/api/v1',
   timeout: 100000
 })
 
@@ -31,7 +31,7 @@ const redirectUrl = () => {
 }
 
 instance.interceptors.request.use(config => {
-  config.headers.Authentication = useUserStore().userInfo.token
+  config.headers.Authorization = 'ImE4ZDE1MTgyMDk1YzExZjA5N2NhMDI0MmFjMTgwMDA2Ig.Z-J5Kg.mXOMgkPUGSsXyw0CUR3DlMRC-Fc'
   return config
 }, function (error) {
   return Promise.reject(error)

+ 1 - 1
src/store/index.ts

@@ -2,7 +2,7 @@
 
 export { useDesignStore } from './modules/designStore/designStore'
 
-export { useUserStore } from './modules/user/index'
+export { useUserStore } from './userStore'
 
 export { useRouterTravelStore } from './modules/commonStore/routerTravelStore'
 

+ 91 - 91
src/store/modules/user/index.ts → src/store/userStore.ts

@@ -1,91 +1,91 @@
-import { Modal, message } from 'ant-design-vue'
-import { defineStore } from 'pinia'
-import { reactive } from 'vue'
-
-import { UserController } from '@/controller'
-import { useRouter, useRoute } from 'vue-router'
-import { routes } from '@/router'
-
-const initState = {
-  id: '',
-  tenantId: '',
-  token: '',
-  email: '',
-  account: '',
-  label: '用户'
-}
-
-export const useUserStore = defineStore('userStore', () => {
-  const router = useRouter()
-
-  const route = useRoute()
-
-  const userInfo = reactive(JSON.parse(window.localStorage.getItem('userInfo') as string) || initState)
-
-  const clearUserInfo = () => {
-    return new Promise((resolve) => {
-      // 做些什么git p
-      message.loading('清除用户信息中......', 0.5)
-      window.localStorage.removeItem('userInfo')
-      setTimeout(() => {
-        resolve(true)
-      }, 500)
-    })
-  }
-
-  const login = async (data: USER.Login) => {
-    // 存储用户信息 跳转页面
-    // const { data: tenant, code } = await UserController.login({
-    //   ...data,
-    //   password: encryptPassWord(data.password)
-    // })
-    // if (code === 200) {
-    //   window.localStorage.setItem('userInfo', JSON.stringify(tenant))
-    //   const userInfoLocal = JSON.parse(window.localStorage.getItem('userInfo') as string)
-    //   userInfo = reactive(userInfoLocal) || initState
-    //   message.success('登录成功')
-    //   if (route.query.redirectUrl) {
-    //     router.push({ path: route.query.redirectUrl as string })
-    //   } else {
-    //     router.push({ path: routes[0].path })
-    //   }
-    // }
-  }
-
-  const openUserInfoModal = () => {
-    Modal.success({
-      title: userInfo.label + '的信息',
-      content: '需要想到优雅的方式来打开个人信息弹窗',
-      cancelText: '取消',
-      okText: '确定',
-      onCancel: () => {},
-      onOk: () => {}
-    })
-  }
-
-  const logout = () => {
-    Modal.confirm({
-      title: '您确定要退出吗?',
-      cancelText: '取消',
-      okText: '确定',
-      onCancel: () => {},
-      onOk: async () => {
-        await clearUserInfo()
-        message.success('登出成功')
-        // 退出
-        router.push({ path: '/login' })
-      }
-    })
-  }
-
-  const encryptPassWord = (password: string) => encodeURI(btoa(password))
-
-  return {
-    userInfo,
-    login,
-    logout,
-    clearUserInfo,
-    openUserInfoModal,
-    encryptPassWord
-  }
-})
+import { Modal, message } from 'ant-design-vue'
+import { defineStore } from 'pinia'
+import { reactive } from 'vue'
+
+import { UserController } from '@/controller'
+import { useRouter, useRoute } from 'vue-router'
+import { routes } from '@/router'
+
+const initState = {
+  id: '',
+  tenantId: '',
+  token: '',
+  email: '',
+  account: '',
+  label: '用户'
+}
+
+export const useUserStore = defineStore('userStore', () => {
+  const router = useRouter()
+
+  const route = useRoute()
+
+  const userInfo = reactive(JSON.parse(window.localStorage.getItem('userInfo') as string) || initState)
+
+  const clearUserInfo = () => {
+    return new Promise((resolve) => {
+      // 做些什么git p
+      message.loading('清除用户信息中......', 0.5)
+      window.localStorage.removeItem('userInfo')
+      setTimeout(() => {
+        resolve(true)
+      }, 500)
+    })
+  }
+
+  const login = async (data: USER.LOGIN) => {
+    // const  {} = await UserController.login(data)
+    // const { data: tenant, code } = await UserController.login({
+    //   ...data,
+    //   password: encryptPassWord(data.password)
+    // })
+    // if (code === 200) {
+    //   window.localStorage.setItem('userInfo', JSON.stringify(tenant))
+    //   const userInfoLocal = JSON.parse(window.localStorage.getItem('userInfo') as string)
+    //   userInfo = reactive(userInfoLocal) || initState
+    //   message.success('登录成功')
+    //   if (route.query.redirectUrl) {
+    //     router.push({ path: route.query.redirectUrl as string })
+    //   } else {
+    //     router.push({ path: routes[0].path })
+    //   }
+    // }
+  }
+
+  const openUserInfoModal = () => {
+    Modal.success({
+      title: userInfo.label + '的信息',
+      content: '需要想到优雅的方式来打开个人信息弹窗',
+      cancelText: '取消',
+      okText: '确定',
+      onCancel: () => {},
+      onOk: () => {}
+    })
+  }
+
+  const logout = () => {
+    Modal.confirm({
+      title: '您确定要退出吗?',
+      cancelText: '取消',
+      okText: '确定',
+      onCancel: () => {},
+      onOk: async () => {
+        await clearUserInfo()
+        message.success('登出成功')
+        // 退出
+        router.push({ path: '/login' })
+      }
+    })
+  }
+
+  const encryptPassWord = (password: string) => encodeURI(btoa(password))
+
+  return {
+    userInfo,
+    login,
+    logout,
+    clearUserInfo,
+    openUserInfoModal,
+    encryptPassWord
+  }
+})

+ 75 - 0
src/type/chat.d.ts

@@ -0,0 +1,75 @@
+declare namespace CHAT {
+
+  interface DIALOG_PARAMS {
+    'name': string,
+    'icon': string,
+    'language': string,
+    'prompt_config': {
+      'empty_response': string,
+      'prologue': string,
+      'quote': boolean,
+      'keyword':boolean,
+      'tts':boolean,
+      'system':string
+      'refine_multiturn':boolean,
+      'use_kg':boolean,
+      'reasoning':boolean,
+      'parameters': {'key':string, 'optional':boolean }[]
+      'llm_id':string
+      'llm_setting': {
+        'temperature': number,
+        'top_p': number
+        'presence_penalty':number
+        'frequency_penalty':number
+      },
+      'similarity_threshold': number
+      'vector_similarity_weight': number
+      'top_n': number
+    }
+  }
+
+  interface DIALOG {
+    id: string
+    name: string
+    description: string
+  }
+
+  interface DELTA {
+    'content': string,
+    'role': string,
+    'function_call': null,
+    'tool_calls': null
+}
+
+  interface INFO {
+    'id': string,
+    'choices': {
+      'delta': DELTA,
+      'finish_reason': null,
+      'index': number,
+      'logprobs': null
+    }[],
+    'created': string,
+    'model': 'model',
+    'object': string,
+    'system_fingerprint': string,
+    'usage': null
+  }
+
+  interface CONVERSATION_PARAMS {
+    'dialog_id'?: string,
+    'name': string,
+    'is_new': boolean,
+    'conversation_id': string,
+    'message'?: {'role': string, 'content': string}[]
+  }
+
+  interface CONVERSATION {
+    'dialog_id'?: string,
+    'name': string,
+    'create_time': number
+    'is_new': boolean,
+    'conversation_id': string,
+    'message'?: {'role': string, 'content': string}[]
+  }
+}

+ 2 - 1
src/type/user.d.ts

@@ -1,5 +1,6 @@
 declare namespace USER {
-  interface Login {
+  interface LOGIN {
+    email: string
     password: string,
   }
 }

+ 55 - 6
yarn.lock

@@ -2659,6 +2659,11 @@ argparse@^1.0.7:
   dependencies:
     sprintf-js "~1.0.2"
 
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
 array-buffer-byte-length@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz"
@@ -3803,11 +3808,16 @@ d3@^7.2.0:
     d3-transition "3"
     d3-zoom "3"
 
-dayjs@^1.10.5, dayjs@^1.11.7:
+dayjs@^1.10.5:
   version "1.11.7"
   resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz"
   integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
 
+dayjs@^1.11.13:
+  version "1.11.13"
+  resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
+  integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
+
 debug@2.6.9:
   version "2.6.9"
   resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz"
@@ -4106,7 +4116,7 @@ entities@^2.0.0:
   resolved "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz"
   integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
 
-entities@^4.5.0:
+entities@^4.4.0, entities@^4.5.0:
   version "4.5.0"
   resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz"
   integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
@@ -4733,10 +4743,10 @@ flatted@^3.1.0:
   resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz"
   integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
 
-flicker-vue-hooks@^1.0.60:
-  version "1.0.60"
-  resolved "https://registry.npmmirror.com/flicker-vue-hooks/-/flicker-vue-hooks-1.0.60.tgz"
-  integrity sha512-G3SiubpNaG6Egm69lK/e7SvTYuiaUZT88/CfHseC7BauMGnV97lgbJFzcU/IixSGQQOmxEh6ZOTRa13uPL372Q==
+flicker-vue-hooks@^1.0.64:
+  version "1.0.64"
+  resolved "https://registry.npmmirror.com/flicker-vue-hooks/-/flicker-vue-hooks-1.0.64.tgz#25864025e96298a874c7a3e30f51c5dfcf5a950f"
+  integrity sha512-rnY2JiNqB8Z+Ekm+Khz8DARtYsi6q5RyS4Sa9QZTRnRD4/ZJjBiXDakORhZvhbmhVglxIVq+yH9KQa0bMPFO2g==
   dependencies:
     vue "^3.4.15"
 
@@ -5096,6 +5106,11 @@ highlight.js@^10.7.1:
   resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz"
   integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
 
+highlight@^0.2.4:
+  version "0.2.4"
+  resolved "https://registry.npmmirror.com/highlight/-/highlight-0.2.4.tgz#8ac02875b03f5935e0675852b76cfe1fd58e0dff"
+  integrity sha512-TEcWU6BolpDYIaVD91KmaYe/kRZwOmQlLWZGO8DK+Cs555+7mawk2KUnF/dBwcLnrvlCDk/xC+BXfz7Zva+Jfg==
+
 hosted-git-info@^2.1.4:
   version "2.8.9"
   resolved "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz"
@@ -5805,6 +5820,13 @@ lines-and-columns@^1.1.6:
   resolved "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
   integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
 
+linkify-it@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421"
+  integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==
+  dependencies:
+    uc.micro "^2.0.0"
+
 lint-staged@^11.1.2:
   version "11.2.6"
   resolved "https://registry.npmmirror.com/lint-staged/-/lint-staged-11.2.6.tgz"
@@ -6016,6 +6038,18 @@ make-dir@^3.0.2, make-dir@^3.1.0:
   dependencies:
     semver "^6.0.0"
 
+markdown-it@^14.1.0:
+  version "14.1.0"
+  resolved "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45"
+  integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==
+  dependencies:
+    argparse "^2.0.1"
+    entities "^4.4.0"
+    linkify-it "^5.0.0"
+    mdurl "^2.0.0"
+    punycode.js "^2.3.1"
+    uc.micro "^2.1.0"
+
 marked@^4.0.17:
   version "4.3.0"
   resolved "https://registry.npmmirror.com/marked/-/marked-4.3.0.tgz"
@@ -6033,6 +6067,11 @@ mdn-data@2.0.14:
   resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz"
   integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
 
+mdurl@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0"
+  integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==
+
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz"
@@ -7011,6 +7050,11 @@ pump@^3.0.0:
     end-of-stream "^1.1.0"
     once "^1.3.1"
 
+punycode.js@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7"
+  integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==
+
 punycode@^2.1.0:
   version "2.3.0"
   resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.0.tgz"
@@ -8128,6 +8172,11 @@ typescript@~4.5.5:
   resolved "https://registry.npmmirror.com/typescript/-/typescript-4.5.5.tgz"
   integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
 
+uc.micro@^2.0.0, uc.micro@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee"
+  integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==
+
 unbox-primitive@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz"