소스 검색

refactor(AIRobot): 优化消息展示逻辑

- 移除了消息内容中的用户名显示
-调整了消息发送逻辑,移除了不必要的用户名字段
-优化了代码结构,提高了组件的可维护性
hongk 1 주 전
부모
커밋
efa6c43b2c

+ 1 - 1
projects/base/package.json

@@ -35,6 +35,6 @@
     "url": "http://112.124.9.106:10300/one_health/web.git"
   },
   "dependencies": {
-    "ai-robot": "^1.0.1"
+    "ai-robot": "^1.0.2"
   }
 }

+ 1 - 9
projects/base/src/views/Demo.vue

@@ -6,7 +6,7 @@
 </template>
 
 <script setup lang="ts">
-import AIRobot from 'ai-robot'
+import AIRobot from '@common/package/AIRobot/AIRobot.vue'
 import request from 'axios'
 import { ref } from 'vue'
 
@@ -38,14 +38,6 @@ const onMessage = (message) => {
     )
     .then((res) => {
       const answer = res.data.answer
-
-      // const res = await request.post('http://112.124.9.106:8092/chat', {
-      //   message: message.body.text,
-      //   session_id: sessionId.value
-      // })
-      // const answer = res.data.reply
-      // sessionId.value = res.data.session_id
-
       let text = answer
 
       // 模拟业务接口处理

+ 0 - 15
projects/base/types/components.d.ts

@@ -1,15 +0,0 @@
-declare module 'vue' {
-  export interface GlobalComponents {
-    Icon: (typeof import('@common/src/components/Icon/index'))['Icon']
-    Permission: (typeof import('@common/src/components/Permission/index'))['Permission']
-    BaseButton: (typeof import('@common/src/components/Button/index'))['BaseButton']
-    BackButton: (typeof import('@common/src/business-components/BackButton.vue'))['BackButton']
-  }
-}
-
-export {}
-
-declare global {
-  const BaseButton: typeof import('@common/src/components/Button/index').BaseButton
-  const BackButton: typeof import('@common/src/business-components/BackButton.vue').BackButton
-}

+ 0 - 7
projects/base/types/ele.d.ts

@@ -1,7 +0,0 @@
-export {}
-
-// 忽略动态引入时的报错提示
-declare global {
-  const ElMessage: typeof import('element-plus').ElMessage
-  const ElMessageBox: typeof import('element-plus').ElMessageBox
-}

+ 0 - 14
projects/base/types/env.d.ts

@@ -1,14 +0,0 @@
-/// <reference types="vite/client" />
-
-declare module '*.vue' {
-  import { DefineComponent } from 'vue'
-
-  const component: DefineComponent<{}, {}, any>
-  export default component
-}
-
-declare global {
-  interface ImportMeta {
-    readonly env: ImportMetaEnv
-  }
-}

+ 0 - 101
projects/base/types/global.d.ts

@@ -1,101 +0,0 @@
-import type { CSSProperties } from 'vue'
-import { RawAxiosRequestHeaders } from 'axios'
-declare global {
-  declare interface Fn<T = any> {
-    (...arg: T[]): T
-  }
-
-  declare type Nullable<T> = T | null
-
-  declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>
-
-  declare type Recordable<T = any, K = string> = Record<K extends null | undefined ? string : K, T>
-
-  declare type RemoveReadonly<T> = {
-    -readonly [P in keyof T]: T[P]
-  }
-
-  declare type ComponentRef<T> = InstanceType<T>
-
-  declare type LocaleType = 'zh-CN' | 'en'
-
-  declare type TimeoutHandle = ReturnType<typeof setTimeout>
-  declare type IntervalHandle = ReturnType<typeof setInterval>
-
-  declare type ElementPlusInfoType = 'success' | 'info' | 'warning' | 'danger'
-
-  declare type LayoutType = 'classic' | 'topLeft' | 'top' | 'cutMenu'
-
-  declare type AxiosContentType =
-    | 'application/json'
-    | 'application/x-www-form-urlencoded'
-    | 'multipart/form-data'
-    | 'text/plain'
-
-  declare type AxiosMethod = 'get' | 'post' | 'delete' | 'put'
-
-  declare type AxiosResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
-
-  declare interface AxiosConfig {
-    params?: any
-    data?: any
-    url?: string
-    method?: AxiosMethod
-    headers?: RawAxiosRequestHeaders
-    responseType?: AxiosResponseType
-  }
-
-  declare interface IResponse<T = any> {
-    code: number
-    data: T extends any ? T : T & any
-    success: boolean
-    message: string
-  }
-
-  declare interface ThemeTypes {
-    elColorPrimary?: string
-    leftMenuBorderColor?: string
-    leftMenuBgColor?: string
-    leftMenuBgLightColor?: string
-    leftMenuBgActiveColor?: string
-    leftMenuCollapseBgActiveColor?: string
-    leftMenuTextColor?: string
-    leftMenuTextActiveColor?: string
-    logoTitleTextColor?: string
-    logoBorderColor?: string
-    topHeaderBgColor?: string
-    topHeaderTextColor?: string
-    topHeaderHoverColor?: string
-    topToolBorderColor?: string
-  }
-
-  declare interface ImportMetaEnv {
-    readonly VITE_NODE_ENV: string
-    readonly VITE_APP_TITLE: string
-    readonly VITE_API_BASE_PATH: string
-    readonly VITE_BASE_PATH: string
-    readonly VITE_DROP_DEBUGGER: string
-    readonly VITE_DROP_CONSOLE: string
-    readonly VITE_SOURCEMAP: string
-    readonly VITE_OUT_DIR: string
-    readonly VITE_USE_BUNDLE_ANALYZER: string
-    readonly VITE_USE_ALL_ELEMENT_PLUS_STYLE: string
-    readonly VITE_USE_CSS_SPLIT: string
-    readonly VITE_USE_ONLINE_ICON: string
-    readonly VITE_ICON_PREFIX: string
-    readonly VITE_HIDE_GLOBAL_SETTING: string
-  }
-}
-
-// 代码字典的声明
-declare interface ICodeItem {
-  value: string
-  label: string
-}
-
-// 自定义的window全局属性
-declare interface Window {
-  CITY_CODE?: string;
-  dateRangeProps: Recordable // 根据实际类型调整
-  APP_FUN_ID: string
-}

+ 0 - 78
projects/base/types/router.d.ts

@@ -1,78 +0,0 @@
-import type { RouteRecordRaw } from 'vue-router'
-import { defineComponent } from 'vue'
-
-/**
-* redirect: noredirect        当设置 noredirect 的时候该路由在面包屑导航中不可被点击
-* name:'router-name'          设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
-* meta : {
-    hidden: true              当设置 true 的时候该路由不会再侧边栏出现 如404,login等页面(默认 false)
-
-    alwaysShow: true          当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式,
-                              只有一个时,会将那个子路由当做根路由显示在侧边栏,
-                              若你想不管路由下面的 children 声明的个数都显示你的根路由,
-                              你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,
-                              一直显示根路由(默认 false)
-
-    title: 'title'            设置该路由在侧边栏和面包屑中展示的名字
-
-    icon: 'svg-name'          设置该路由的图标
-
-    noCache: true             如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
-
-    breadcrumb: false         如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)
-
-    affix: true               如果设置为true,则会一直固定在tag项中(默认 false)
-
-    noTagsView: true          如果设置为true,则不会出现在tag中(默认 false)
-
-    activeMenu: '/dashboard'  显示高亮的路由路径
-
-    canTo: true               设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)
-
-    permission: ['edit','add', 'delete']    设置该路由的权限
-  }
-**/
-
-interface RouteMetaCustom extends Record<string | number | symbol, unknown> {
-  hidden?: boolean
-  alwaysShow?: boolean
-  title?: string
-  icon?: string
-  noCache?: boolean
-  breadcrumb?: boolean
-  affix?: boolean
-  activeMenu?: string
-  noTagsView?: boolean
-  canTo?: boolean
-  permission?: string[]
-}
-
-declare module 'vue-router' {
-  interface RouteMeta extends RouteMetaCustom {}
-}
-
-type Component<T = any> =
-  | ReturnType<typeof defineComponent>
-  | (() => Promise<typeof import('*.vue')>)
-  | (() => Promise<T>)
-
-declare global {
-  declare interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta' | 'children'> {
-    name: string
-    meta: RouteMetaCustom
-    component?: Component | string
-    children?: AppRouteRecordRaw[]
-    props?: Recordable
-    fullPath?: string
-  }
-
-  declare interface AppCustomRouteRecordRaw
-    extends Omit<RouteRecordRaw, 'meta' | 'component' | 'children'> {
-    name: string
-    meta: RouteMetaCustom
-    component: string
-    path: string
-    redirect: string
-    children?: AppCustomRouteRecordRaw[]
-  }
-}

+ 2 - 1
projects/package/AIRobot/AIRobot.vue

@@ -63,6 +63,7 @@
 <script setup lang="ts">
 import AiChat from './components/AiChat.vue'
 import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
+import {Recordable} from "vite-plugin-mock";
 
 const props = defineProps({
   a: {
@@ -168,7 +169,7 @@ const handleNewChat = () => {
 }
 
 // 发送消息
-const emit = defineEmits(['on-handle-send'])
+const emit = defineEmits(['onMessage'])
 const handleSend = (message) => {
   emit('onMessage', message)
 }

+ 4 - 38
projects/package/AIRobot/components/AiChat.vue

@@ -18,11 +18,6 @@
             <img :src="message.author === 'sender' ? ManAvatar : AiAvatar" alt="" />
           </div>
           <div class="chat-message-content">
-            <!-- 用户名 -->
-            <div class="chat-message-content__name">
-              {{ message.name }}
-              <!--              <span v-if="message.author !== 'sender'" class="tag">AI助手</span>-->
-            </div>
             <!-- 消息主内容 -->
             <div class="chat-message-content__message">
               <!-- 消息部分 -->
@@ -49,6 +44,7 @@ import Thinking from './Thinking.vue'
 import { marked } from 'marked'
 
 import { ref, watch, onUnmounted, nextTick } from 'vue'
+import {Recordable} from "vite-plugin-mock";
 
 const props = defineProps({
   isOpen: {
@@ -121,7 +117,6 @@ const init = () => {
     date: newRecordTime,
     showTime: newRecordTime,
     author: 'replyer',
-    name: aiName,
     ignore: true
   }
   messageList.value.push(record)
@@ -165,8 +160,7 @@ const setThinking = (bol: boolean = true) => {
   if (bol) {
     messageList.value.push({
       body: { component: Thinking },
-      author: 'replyer',
-      name: aiName
+      author: 'replyer'
     })
   } else messageList.value.pop()
 }
@@ -175,7 +169,7 @@ const sessionId = ref('')
 // 发送消息
 const handleMessageReceived = async (message) => {
   // 展示用户问题
-  await addMessage({ ...message, name: '用户' })
+  await addMessage(message)
   setTimeout(() => {
     messageScroll()
   })
@@ -215,8 +209,7 @@ const handleMessageResponse = async (message) => {
   const record = {
     body: { ...message },
     date: new Date().getTime(),
-    author: 'replyer',
-    name: aiName
+    author: 'replyer'
   }
   await addMessage(record)
 
@@ -231,7 +224,6 @@ const newChat = () => {
   messageList.value.push({
     body: { text: '已开启新对话~' },
     author: 'replyer',
-    name: aiName,
     ignore: true
   })
 }
@@ -453,28 +445,6 @@ defineExpose({ newChat, handleMessageResponse })
       .chat-message-content {
         width: 100%;
         margin-left: 4px;
-
-        &__name {
-          font-size: 14px;
-          font-weight: 400;
-          color: #7c838e;
-          line-height: 20px;
-          margin-bottom: 4px;
-          margin-left: 3px;
-
-          .tag {
-            font-size: 11px;
-            color: #0482ff;
-            line-height: 17px;
-            padding: 0 4px;
-            background: linear-gradient(270deg, #8afffa, #a5ffec);
-            border-radius: 2px;
-            box-shadow: 0px 1px 4px 0px rgba(255, 255, 255, 0.2) inset;
-            & + .tag {
-              margin-left: 12px;
-            }
-          }
-        }
         &__message {
           display: inline-block;
           max-width: 85%;
@@ -510,10 +480,6 @@ defineExpose({ newChat, handleMessageResponse })
         order: 1;
         text-align: right;
         margin-right: 4px;
-
-        &__name {
-          margin-right: 3px;
-        }
       }
 
       .chat-message-content__message {

+ 16 - 16
projects/package/AIRobot/components/ChatBottom.vue

@@ -88,7 +88,7 @@ const throttleHandle = _throttle(function () {
 const keydownEnter = (event) => {
   const div = textareaRef.value
   // 返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置
-  const selection = window.getSelection()
+  const selection = window.getSelection() as Recordable
   // 返回一个包含当前选区内容的区域对象。
   const range = selection.getRangeAt(0)
   // startOffset:开始偏移量,选定文本的第一个字符在文本输入字段中的位置
@@ -110,21 +110,21 @@ const keydownEnter = (event) => {
     event.preventDefault()
   }
 }
-const getFile = (e) => {
-  const file = e.target.files[0]
-  const url = window.URL.createObjectURL(file)
-  emit('handleSend', {
-    component: file.type.startsWith('image/') ? 'ImgMessage' : 'FileMessage',
-    messageProps: {
-      url,
-      // type: file.type.startsWith('image/') ? 'image' : 'file',
-      fileName:
-        file.name.length > 25 ? `${file.name.slice(0, 10)}...${file.name.slice(-15)}` : file.name,
-      fileSize: `${file.size / 1000}k`
-    }
-  })
-  e.target.value = ''
-}
+// const getFile = (e) => {
+//   const file = e.target.files[0]
+//   const url = window.URL.createObjectURL(file)
+//   emit('handleSend', {
+//     component: file.type.startsWith('image/') ? 'ImgMessage' : 'FileMessage',
+//     messageProps: {
+//       url,
+//       // type: file.type.startsWith('image/') ? 'image' : 'file',
+//       fileName:
+//         file.name.length > 25 ? `${file.name.slice(0, 10)}...${file.name.slice(-15)}` : file.name,
+//       fileSize: `${file.size / 1000}k`
+//     }
+//   })
+//   e.target.value = ''
+// }
 const handleMousedown = () => {
   recording.value = true
   recordingTime.value = Date.now()

+ 3 - 3
projects/package/AIRobot/components/Recording.vue

@@ -1,13 +1,13 @@
 <!-- 语音识别 -->
 <template>
   <div class="ai-recording">
-    <div v-for="(i, index) in 31" :key="index" class="ai-recording-bar"></div>
+    <div v-for="(_i, index) in 31" :key="index" class="ai-recording-bar"></div>
   </div>
 </template>
 
 <script setup lang="ts">
 import { IatRecorder } from '../utils/voice'
-const iatRecorder = new IatRecorder()
+const iatRecorder:Recordable = new IatRecorder()
 import { ref, onMounted, nextTick, onUnmounted } from 'vue'
 
 const text = ref('')
@@ -32,7 +32,7 @@ function stopRecording() {
 const emit = defineEmits(['on-complete'])
 onMounted(() => {
   nextTick(() => {
-    iatRecorder.onWillStatusChange = (oldStatus, status, audioData) => {
+    iatRecorder.onWillStatusChange = (_oldStatus, status, _audioData) => {
       console.log(`status===${status}`)
       // 可以在这里进行页面中一些交互逻辑处理:倒计时(听写只有60s),录音的动画,按钮交互等
       // status: null init ing end

+ 1 - 1
projects/package/AIRobot/package.json

@@ -1,6 +1,6 @@
 {
   "name": "ai-robot",
-  "version": "1.0.1",
+  "version": "1.0.2",
   "description": "一款智能问答机器人模块",
   "author": "hongkai",
   "main": "dist/index.es.js",

+ 2 - 2
tsconfig.json

@@ -32,6 +32,6 @@
       "vite-plugin-svg-icons/client"
     ]
   },
-  "include": ["src", "common", "types/**/*.d.ts"]
-  // "exclude": ["dist", "node_modules"]
+  "include": ["src", "projects/**/*","types/**/*.d.ts"],
+  "exclude": ["dist", "node_modules"]
 }