Browse Source

fix:增加图像分析;其他会话根据需要增加modelType;

dxj
李朋徽 1 year ago
parent
commit
cc6deb57d4
  1. 16
      src/api/base/file.ts
  2. 25
      src/api/base/message.ts
  3. 21
      src/components/AppMessage/index.vue
  4. 4
      src/design/public.scss
  5. 9
      src/enums/messageEnum.ts
  6. 12
      src/layout/AppMenu/index.vue
  7. 2
      src/store/moules/messageStore/index.ts
  8. 2
      src/utils/axios/Axios.ts
  9. 1
      src/utils/axios/index.ts
  10. 9
      src/views/conversation/index.vue
  11. 151
      src/views/repository/index.vue
  12. 11
      src/views/role/index.vue
  13. 18
      src/views/textToImage/index.vue
  14. 195
      src/views/visualAnalysis/index.vue
  15. 7
      types/axios.d.ts

16
src/api/base/file.ts

@ -1,13 +1,15 @@
import { defHttp } from '@/utils/axios/index'
import type { AxiosProgressEvent } from 'axios'
import type { UploadFileParams } from '/#/axios'
import { ContentTypeEnum } from '@/enums/httpEnum'
import type { UploadFileResult } from '/#/axios'
export function uploadApi(params: UploadFileParams, onUploadProgress: (progressEvent: AxiosProgressEvent) => void) {
return defHttp.uploadFile(
export function uploadApi(data: FormData): Promise<UploadFileResult> {
return defHttp.post(
{
url: '/qn/iot-system/oss/endpoint/put-file',
onUploadProgress,
url: '/hulk-system/hulk-resource/oss/endpoint/put-file',
headers: {
'Content-Type': ContentTypeEnum.FORM_DATA,
},
data,
},
params,
)
}

25
src/api/base/message.ts

@ -80,7 +80,7 @@ export async function stopMessage(data: { conversationId: string }) {
}
/**
* @description
* @description modelTypegpt3.5gpt4
*/
export async function sendTextToText(data: {
conversationId: string
@ -90,12 +90,12 @@ export async function sendTextToText(data: {
return defHttp.post({
url: `/open-chat/chat/session`,
data,
timeout: 60 * 1000,
timeout: 120 * 1000,
})
}
/**
* @description
* @description modelType
*/
export async function sendTextToImage(data: {
conversationId: string
@ -104,7 +104,7 @@ export async function sendTextToImage(data: {
return defHttp.post({
url: `/open-gpts/gpts/getDallEImages`,
data,
timeout: 60 * 1000,
timeout: 120 * 1000,
})
}
@ -118,7 +118,22 @@ export async function sendRepository(data: {
return defHttp.post({
url: `/open-gpts/gpts/getQanythingStreamChat`,
data,
timeout: 60 * 1000,
timeout: 120 * 1000,
})
}
/**
* @description modelType
*/
export async function sendVisualAnalysis(data: {
conversationId: string
question: string
fileUrl: string
}) {
return defHttp.post({
url: `/open-gpts/gpts/getImageVision`,
data,
timeout: 120 * 1000,
})
}

21
src/components/AppMessage/index.vue

@ -11,6 +11,10 @@ import { copyText } from '@/utils/copyTextToClipboard'
import { useMessageStore } from '@/store/moules/messageStore/index'
const props = defineProps({
height: {
type: String,
default: 'calc(100% - 120px)',
},
list: {
type: Array as PropType<MessageItem[]>,
default: () => [],
@ -121,7 +125,10 @@ onMounted(async () => {
<div v-for="(item, index) in list" :key="item.time + index" class="item">
<div v-if="item.messageType === MessageTypeEnum.USER" class="user">
<div class="content">
{{ item.content }}
<MdPreview
editor-id="preview-only-ai"
:model-value="item.content"
/>
</div>
<img class="icon-user" src="@/assets/images/conversation/user.png" alt="">
</div>
@ -136,11 +143,12 @@ onMounted(async () => {
conversationData?.type === MenuTypeEnum.TEXT_TO_TEXT
|| conversationData?.type === MenuTypeEnum.ROLE
|| conversationData?.type === MenuTypeEnum.REPOSITORY
|| conversationData?.type === MenuTypeEnum.VISUAL_ANALYSIS
"
editor-id="preview-only-ai"
:model-value="item.content"
/>
<div v-if="conversationData?.type === MenuTypeEnum.TEXT_TO_IMAGE">
<div v-if="conversationData?.type === MenuTypeEnum.TEXT_TO_IMAGE" class="pt-2 pb-2">
<Image
:width="200"
:height="200"
@ -175,7 +183,7 @@ onMounted(async () => {
<style lang="scss" scoped>
@include app('message') {
height: calc(100% - 120px);
height: v-bind(height);
overflow: auto;
box-sizing: border-box;
:deep(.md-editor-preview) {
@ -199,9 +207,12 @@ onMounted(async () => {
justify-content: flex-end;
.content {
max-width: calc(100% - 110px);
padding: 10px 15px;
padding: 5px 15px;
background: #edf3ff;
border-radius: 10px;
div {
background: #edf3ff;
}
}
.icon-user {
width: 40px;
@ -223,7 +234,7 @@ onMounted(async () => {
margin-right: 5px;
}
.content {
padding: 10px 15px;
padding: 5px 15px;
background: #ffffff;
box-shadow: 0px 2px 10px 0px rgba(128, 135, 152, 0.4);
border-radius: 10px;

4
src/design/public.scss

@ -17,13 +17,13 @@
}
::-webkit-scrollbar-thumb {
background-color: rgba($color: #cccccc, $alpha: 0.4);
background-color: var(--border-color);
border-radius: 2px;
// box-shadow: inset 0 0 6px rgba($color: #000000, $alpha: 0.2);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--border-color);
background-color: rgba($color: #cccccc, $alpha: 0.4);
}
// =================================

9
src/enums/messageEnum.ts

@ -32,4 +32,13 @@ export enum ModelTypeEnum {
// GPT3.5
GPT3 = 2,
// 文生图 DALL·E 3
DALL_E3 = 3,
// 视觉分析 gpt-vision
VISION = 4,
// 知识库 QAnything
QANYTHING = 5,
}

12
src/layout/AppMenu/index.vue

@ -26,12 +26,12 @@ const menu = ref<MenuItem[]>([
path: '/role',
key: MenuTypeEnum.ROLE,
},
// {
// name: '',
// icon: 'repository',
// path: '/repository',
// key: MenuTypeEnum.REPOSITORY,
// },
{
name: '知识库',
icon: 'repository',
path: '/repository',
key: MenuTypeEnum.REPOSITORY,
},
{
name: '图像分析',
icon: 'visual_analysis',

2
src/store/moules/messageStore/index.ts

@ -31,7 +31,7 @@ export const useMessageStore = defineStore('useMessageStore', {
this.messageStatus = status
},
setConversationData(data: ConversationData) {
setConversationData(data: ConversationData | undefined) {
this.conversationData = data
},

2
src/utils/axios/Axios.ts

@ -129,7 +129,7 @@ export class VAxios {
* @description:
*/
uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
const formData = new window.FormData()
const formData = new FormData()
const customFilename = params.name || 'file'
if (params.filename)

1
src/utils/axios/index.ts

@ -27,6 +27,7 @@ const notDecryptWhiteList = [
'/open-chat/chat/session',
'/open-gpts/gpts/getDallEImages',
'/open-chat/chat/stopGenerate',
'/hulk-system/hulk-resource/oss/endpoint/put-file',
]
/**

9
src/views/conversation/index.vue

@ -203,10 +203,11 @@ async function getHistoryMessage() {
spinning.value = true
historyMessageParams.value.conversationId = conversationData.value.id
const res = await historyMessage(historyMessageParams.value)
messageStore.setMessageClear()
spinning.value = false
if (!res || !res.records || !res.records.length) {
conversationDefaultShow.value = true
if (!res.records.length && historyMessageParams.value.total === 0) {
conversationDefaultShow.value = true
}
return
}
@ -239,7 +240,7 @@ async function onScrollTop(scrollTop: number) {
return
}
if (historyMessageParams.value.current * historyMessageParams.value.size > historyMessageParams.value.total) {
if (historyMessageParams.value.current * historyMessageParams.value.size >= historyMessageParams.value.total) {
return
}
@ -376,6 +377,7 @@ onMounted(async () => {
onUnmounted(() => {
messageStore.setMessageClear()
messageStore.setConversationData(undefined)
useMqtt().end()
})
@ -441,6 +443,7 @@ onBeforeRouteLeave(() => {
<AppMessage
v-if="!conversationDefaultShow && appMessageShow"
ref="appMessageRef"
:key="MenuTypeEnum.TEXT_TO_TEXT"
class="pl-27 pr-5"
:list="messageList"
@on-scroll-top="onScrollTop"

151
src/views/repository/index.vue

@ -10,14 +10,19 @@ import { AppConversationDefault } from '@/components/AppConversationDefault'
import { AppUserInfo } from '@/components/AppUserInfo'
import { SubMenuActionEnum } from '@/components/AppSubMenuList/index.d'
import type { SubMenuItem } from '@/components/AppSubMenuList/index.d'
import type { PictureType } from '@/components/AppPicture/index.d'
import { AppTextarea } from '@/components/AppTextarea'
import { AppMessage } from '@/components/AppMessage'
import type { MessageItem } from '@/components/AppMessage/index.d'
import type { TopPickItem } from '@/components/AppTopPicks/index.d'
import { AppModelSelect } from '@/components/AppModelSelect'
import type { ModelSelect } from '@/components/AppModelSelect/index.d'
import { useMessageStore } from '@/store/moules/messageStore/index'
import { MenuTypeEnum } from '@/enums/menuEnum'
import { MessageStatusEnum, MessageTypeEnum } from '@/enums/messageEnum'
import { addMessage, conversationList, historyMessage, removeMessage, sendRepository, updateMessage } from '@/api/base/message'
import { MessageStatusEnum, MessageTypeEnum, ModelTypeEnum } from '@/enums/messageEnum'
import { addMessage, conversationList, conversationToTop, historyMessage, removeMessage, sendRepository, stopMessage, updateMessage } from '@/api/base/message'
import { getAppList, getRole } from '@/api/base/role'
import { useMqtt } from '@/hooks/useMqtt'
import { useMessage } from '@/hooks/useMessage'
@ -26,11 +31,20 @@ const { createConfirm } = useMessage()
const messageStore = useMessageStore()
const sendBtnLoading = ref(false)
const subMenuActiveIndex = ref(0) //
const subMenuActiveIndex = ref(-1) //
const subMenuActionIndex = ref(-1) //
const subMenuList = ref<SubMenuItem[]>([])
const subMenuInputValue = ref<string>('')
const appMessageRef = ref()
const modelOptions: ModelSelect[] = [
{
label: '知识库',
value: ModelTypeEnum.QANYTHING,
},
]
const modelIndex = ref(0)
const messageList = computed(() => messageStore.getMessageList)
const messageStatus = computed(() => messageStore.getMessageStatus)
const conversationData = computed(() => messageStore.getConversationData)
@ -40,6 +54,7 @@ const historyMessageParams = ref({
size: 10,
total: 0,
})
const leadData = ref({
title: '你好,我是青鸟语言大模型-同聪~',
subTitles: [
@ -67,6 +82,7 @@ watch(
*/
function handlesubMenuActionIndex(type: SubMenuActionEnum, item: SubMenuItem, index: number) {
subMenuActionIndex.value = index
const subMenuActionData = subMenuList.value[index]
if (type === SubMenuActionEnum.EDIT) {
subMenuList.value.forEach((item) => {
item.actionType = SubMenuActionEnum.NOT
@ -81,37 +97,56 @@ function handlesubMenuActionIndex(type: SubMenuActionEnum, item: SubMenuItem, in
content: `确定要删除${item.title}会话吗?`,
iconType: 'warning',
onOk: () => {
removeMessage(item.id).then(() => {
removeMessage(item.id).then(async () => {
message.success('删除成功')
getConversationList()
subMenuActiveIndex.value = -1
conversationDefaultShow.value = true
await getConversationList()
})
},
})
}
if (type === SubMenuActionEnum.TO_TOP) {
conversationToTop(item.id).then(async () => {
message.success('置顶成功')
await getConversationList()
const index = subMenuList.value.findIndex(item => item.id === subMenuActionData.id)
await handleSubMenuChange(index)
getHistoryMessage()
})
}
}
/**
* @description: 切换会话
*/
async function handleSubMenuChange(index: number) {
async function handleSubMenuChange(index: number, item?: SubMenuItem) {
if (messageStatus.value !== MessageStatusEnum.END) {
message.warn('请先结束对话')
return
}
if (!subMenuList.value.length) {
return
}
subMenuActiveIndex.value = index
historyMessageParams.value.current = 1
historyMessageParams.value.total = 0
messageStore.setConversationData(subMenuList.value[subMenuActiveIndex.value])
messageStore.setMessageClear()
const modelIdx = modelOptions.findIndex(v => v.value === Number(subMenuList.value[subMenuActiveIndex.value].modelType))
if (modelIdx === -1) {
message.warn('未找到该对话下的模型信息')
return
}
modelIndex.value = modelIdx
useMqtt().end()
useMqtt().connect()
getHistoryMessage()
//
appMessageShow.value = false
setTimeout(() => {
appMessageShow.value = true
}, 0)
// item
if (item) {
getHistoryMessage()
}
}
/**
@ -120,22 +155,31 @@ async function handleSubMenuChange(index: number) {
async function handleSend(value: string) {
sendBtnLoading.value = true
conversationDefaultShow.value = false
if (!conversationData.value) {
return
}
if (subMenuActiveIndex.value === -1) {
try {
await addMessage({ type: MenuTypeEnum.REPOSITORY, title: '新的对话' })
spinning.value = true
messageStore.setMessageClear()
await addMessage({ type: MenuTypeEnum.REPOSITORY, title: '新的对话', sort: subMenuList.value.length + 1, modelType: modelOptions[modelIndex.value].value })
await getConversationList()
await nextTick()
subMenuActiveIndex.value = subMenuList.value.length - 1
await handleSubMenuChange(subMenuActiveIndex.value)
if (!conversationData.value) {
message.error('对话发送,请稍后重试!')
return
}
sendMessage(conversationData.value.id, value)
spinning.value = false
}
catch (error) {
message.error('新建对话失败,请稍后重试!')
}
}
else {
if (!conversationData.value) {
return
}
sendMessage(conversationData.value.id, value)
}
}
@ -149,10 +193,6 @@ async function getConversationList() {
item.actionType = SubMenuActionEnum.NOT
})
subMenuList.value = res
subMenuActiveIndex.value = 0
if (subMenuList.value.length) {
await handleSubMenuChange(0)
}
}
/**
@ -160,6 +200,8 @@ async function getConversationList() {
*/
async function getHistoryMessage() {
if (!conversationData.value) {
spinning.value = false
conversationDefaultShow.value = true
return
}
spinning.value = true
@ -167,9 +209,11 @@ async function getHistoryMessage() {
const res = await historyMessage(historyMessageParams.value)
spinning.value = false
if (!res || !res.records || !res.records.length) {
if (!res.records.length && historyMessageParams.value.total === 0) {
conversationDefaultShow.value = true
}
return
}
console.log(res)
res.records.forEach((item: any) => {
const itemData: MessageItem = {
@ -184,6 +228,12 @@ async function getHistoryMessage() {
})
conversationDefaultShow.value = false
//
// appMessageShow.value = false
// setTimeout(() => {
// appMessageShow.value = true
// }, 0)
}
/**
@ -194,7 +244,7 @@ async function onScrollTop(scrollTop: number) {
return
}
if (historyMessageParams.value.current * historyMessageParams.value.size > historyMessageParams.value.total) {
if (historyMessageParams.value.current * historyMessageParams.value.size >= historyMessageParams.value.total) {
return
}
@ -211,6 +261,10 @@ async function onScrollTop(scrollTop: number) {
* @description: 发送消息hook
*/
async function sendMessage(conversationId: string, question: string): Promise<void> {
if (!messageStore.getConversationData) {
return
}
conversationDefaultShow.value = false
messageStore.setMessageStatus(MessageStatusEnum.LOADING)
messageStore.setMessagePushItem({
@ -226,9 +280,6 @@ async function sendMessage(conversationId: string, question: string): Promise<vo
avatar: '',
messageStatus: MessageStatusEnum.LOADING,
})
if (!messageStore.getConversationData) {
return
}
sendRepository({
conversationId,
@ -250,6 +301,16 @@ function reloadMessage() {
sendMessage(conversationData.value.id, question)
}
/**
* @description: 停止回答
*/
async function stopMessageFun() {
if (!conversationData.value) {
return
}
await stopMessage({ conversationId: conversationData.value.id })
}
/**
* @description: 点击新建会话按钮
*/
@ -262,6 +323,20 @@ function handleAddMessage() {
subMenuActiveIndex.value = -1
}
/**
* @description: 点击默认看板的精选话题
*/
function handlePick(_index: number, item: TopPickItem) {
handleSend(item.label)
}
/**
* @description: gpt模型切换
*/
function handleModel(index: number) {
modelIndex.value = index
}
//
function handleSubMenuInputAffirm(index: number, item: SubMenuItem, inputValue: string) {
updateMessage({ ...item, title: inputValue }).then(() => {
@ -278,11 +353,18 @@ function handleSubMenuInputBlur(index: number, item: SubMenuItem, inputValue: st
handleSubMenuInputAffirm(index, item, inputValue)
}
onMounted(() => {
getConversationList()
onMounted(async () => {
await getConversationList()
if (subMenuList.value.length) {
subMenuActiveIndex.value = 0
}
await handleSubMenuChange(subMenuActiveIndex.value)
getHistoryMessage()
})
onUnmounted(() => {
messageStore.setMessageClear()
messageStore.setConversationData(undefined)
useMqtt().end()
})
@ -299,7 +381,7 @@ onBeforeRouteLeave(() => {
<AppContainerBox>
<template #subMenu>
<!-- 标题 -->
<AppSubMenuTitle title="知识库会话"></AppSubMenuTitle>
<AppSubMenuTitle></AppSubMenuTitle>
<!-- 按钮 -->
<div class="px-5 mb-5">
@ -325,11 +407,19 @@ onBeforeRouteLeave(() => {
</template>
<template #content>
<Spin :spinning="spinning" wrapper-class-name="app-content-spin">
<AppModelSelect
v-if="conversationDefaultShow"
:active-index="modelIndex"
:options="modelOptions"
@change="handleModel"
>
</AppModelSelect>
<!-- 默认导语 -->
<AppConversationDefault
v-if="conversationDefaultShow"
height="calc(100% - 120px)"
:lead-data="leadData"
height="calc(100% - 120px)"
@handle-pick="handlePick"
>
</AppConversationDefault>
@ -337,6 +427,7 @@ onBeforeRouteLeave(() => {
<AppMessage
v-if="!conversationDefaultShow && appMessageShow"
ref="appMessageRef"
:key="MenuTypeEnum.REPOSITORY"
class="pl-27 pr-5"
:list="messageList"
@on-scroll-top="onScrollTop"
@ -349,7 +440,9 @@ onBeforeRouteLeave(() => {
<AppTextarea
class="pl-52 pr-32 mt-10"
:btn-loading="sendBtnLoading"
:is-stop="false"
@send="handleSend"
@stop-message="stopMessageFun"
></AppTextarea>
</template>
</AppContainerBox>

11
src/views/role/index.vue

@ -180,7 +180,6 @@ async function getHistoryMessage() {
spinning.value = true
historyMessageParams.value.conversationId = conversationData.value.id
const res = await historyMessage(historyMessageParams.value)
messageStore.setMessageClear()
spinning.value = false
if (!res || !res.records || !res.records.length) {
getRoleDecs(String(conversationData.value.roleId)).then((res) => {
@ -208,12 +207,6 @@ async function getHistoryMessage() {
})
conversationDefaultShow.value = false
//
appMessageShow.value = false
setTimeout(() => {
appMessageShow.value = true
}, 0)
}
/**
@ -224,7 +217,7 @@ async function onScrollTop(scrollTop: number) {
return
}
if (historyMessageParams.value.current * historyMessageParams.value.size > historyMessageParams.value.total) {
if (historyMessageParams.value.current * historyMessageParams.value.size >= historyMessageParams.value.total) {
return
}
@ -370,6 +363,7 @@ onMounted(async () => {
onUnmounted(() => {
messageStore.setMessageClear()
messageStore.setConversationData(undefined)
useMqtt().end()
})
@ -432,6 +426,7 @@ onBeforeRouteLeave(() => {
<AppMessage
v-if="!conversationDefaultShow && appMessageShow"
ref="appMessageRef"
:key="MenuTypeEnum.ROLE"
class="pl-27 pr-5"
:list="messageList"
@on-scroll-top="onScrollTop"

18
src/views/textToImage/index.vue

@ -17,7 +17,7 @@ import type { MessageItem } from '@/components/AppMessage/index.d'
import type { TopPickItem } from '@/components/AppTopPicks/index.d'
import { useMessageStore } from '@/store/moules/messageStore/index'
import { MenuTypeEnum } from '@/enums/menuEnum'
import { MessageStatusEnum, MessageTypeEnum } from '@/enums/messageEnum'
import { MessageStatusEnum, MessageTypeEnum, ModelTypeEnum } from '@/enums/messageEnum'
import { addMessage, conversationList, conversationToTop, historyMessage, removeMessage, sendTextToImage, updateMessage } from '@/api/base/message'
import { useMqtt } from '@/hooks/useMqtt'
import { useMessage } from '@/hooks/useMessage'
@ -150,7 +150,7 @@ async function handleSend(value: string) {
try {
spinning.value = true
messageStore.setMessageClear()
await addMessage({ type: MenuTypeEnum.TEXT_TO_IMAGE, title: '新的对话', sort: subMenuList.value.length + 1 })
await addMessage({ type: MenuTypeEnum.TEXT_TO_IMAGE, title: '新的对话', sort: subMenuList.value.length + 1, modelType: ModelTypeEnum.DALL_E3 })
await nextTick()
await getConversationList()
subMenuActiveIndex.value = subMenuList.value.length - 1
@ -197,10 +197,11 @@ async function getHistoryMessage() {
spinning.value = true
historyMessageParams.value.conversationId = conversationData.value.id
const res = await historyMessage(historyMessageParams.value)
messageStore.setMessageClear()
spinning.value = false
if (!res || !res.records || !res.records.length) {
conversationDefaultShow.value = true
if (!res.records.length && historyMessageParams.value.total === 0) {
conversationDefaultShow.value = true
}
return
}
res.records.forEach((item: any) => {
@ -216,12 +217,6 @@ async function getHistoryMessage() {
})
conversationDefaultShow.value = false
//
appMessageShow.value = false
setTimeout(() => {
appMessageShow.value = true
}, 0)
}
/**
@ -232,7 +227,7 @@ async function onScrollTop(scrollTop: number) {
return
}
if (historyMessageParams.value.current * historyMessageParams.value.size > historyMessageParams.value.total) {
if (historyMessageParams.value.current * historyMessageParams.value.size >= historyMessageParams.value.total) {
return
}
@ -334,6 +329,7 @@ onMounted(async () => {
onUnmounted(() => {
messageStore.setMessageClear()
messageStore.setConversationData(undefined)
useMqtt().end()
})

195
src/views/visualAnalysis/index.vue

@ -1,10 +1,10 @@
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { onBeforeRouteLeave } from 'vue-router'
import { Button, Spin, UploadDragger, message } from 'ant-design-vue'
import type { UploadChangeParam } from 'ant-design-vue'
import DefaultImage from '@/assets/images/conversation/default_img3.png'
import { Button, Image, Spin, UploadDragger, message } from 'ant-design-vue'
import type { UploadChangeParam, UploadProps } from 'ant-design-vue'
import { SvgIcon } from '@/components/SvgIcon'
import { AppTextarea } from '@/components/AppTextarea'
import { AppContainerBox } from '@/components/AppContainerBox'
import { AppSubMenuTitle } from '@/components/AppSubMenuTitle'
import { AppSubMenuList } from '@/components/AppSubMenuList'
@ -12,13 +12,17 @@ import { AppConversationDefault } from '@/components/AppConversationDefault'
import { AppUserInfo } from '@/components/AppUserInfo'
import { SubMenuActionEnum } from '@/components/AppSubMenuList/index.d'
import type { SubMenuItem } from '@/components/AppSubMenuList/index.d'
import type { PictureType } from '@/components/AppPicture/index.d'
import { AppMessage } from '@/components/AppMessage'
import type { MessageItem } from '@/components/AppMessage/index.d'
import type { TopPickItem } from '@/components/AppTopPicks/index.d'
import { AppModelSelect } from '@/components/AppModelSelect'
import type { ModelSelect } from '@/components/AppModelSelect/index.d'
import { useMessageStore } from '@/store/moules/messageStore/index'
import { MenuTypeEnum } from '@/enums/menuEnum'
import { MessageStatusEnum, MessageTypeEnum } from '@/enums/messageEnum'
import { addMessage, conversationList, historyMessage, removeMessage, sendRepository, updateMessage } from '@/api/base/message'
import { MessageStatusEnum, MessageTypeEnum, ModelTypeEnum } from '@/enums/messageEnum'
import { addMessage, conversationList, conversationToTop, historyMessage, removeMessage, sendVisualAnalysis, stopMessage, updateMessage } from '@/api/base/message'
import { uploadApi } from '@/api/base/file'
import { useMqtt } from '@/hooks/useMqtt'
import { useMessage } from '@/hooks/useMessage'
@ -28,11 +32,22 @@ const { createConfirm } = useMessage()
const messageStore = useMessageStore()
const sendBtnLoading = ref(false)
const subMenuActiveIndex = ref(0) //
const subMenuActiveIndex = ref(-1) //
const subMenuActionIndex = ref(-1) //
const subMenuList = ref<SubMenuItem[]>([])
const subMenuInputValue = ref<string>('')
const appMessageRef = ref()
const modelOptions: ModelSelect[] = [
{
label: 'DALL-E 3',
value: ModelTypeEnum.DALL_E3,
},
]
const modelIndex = ref(0)
const fileList = ref<string[]>([])
const roleList = ref<PictureType[]>([])
const applyList = ref<PictureType[]>([])
const messageList = computed(() => messageStore.getMessageList)
const messageStatus = computed(() => messageStore.getMessageStatus)
const conversationData = computed(() => messageStore.getConversationData)
@ -42,21 +57,11 @@ const historyMessageParams = ref({
size: 10,
total: 0,
})
const leadData = ref({
title: '你好,我是青鸟语言大模型-同聪~',
subTitles: [
'我可以自由的跟你对话~陪你聊天~帮你想方案~答疑解惑。',
'上传图像至下方,我可以帮你分析哦',
],
image: DefaultImage,
})
const conversationDefaultShow = ref(false)
const appMessageShow = ref(true)
const spinning = ref(true)
const fileList = ref([])
watch(
() => messageStatus.value,
(val) => {
@ -71,6 +76,7 @@ watch(
*/
function handlesubMenuActionIndex(type: SubMenuActionEnum, item: SubMenuItem, index: number) {
subMenuActionIndex.value = index
const subMenuActionData = subMenuList.value[index]
if (type === SubMenuActionEnum.EDIT) {
subMenuList.value.forEach((item) => {
item.actionType = SubMenuActionEnum.NOT
@ -85,37 +91,56 @@ function handlesubMenuActionIndex(type: SubMenuActionEnum, item: SubMenuItem, in
content: `确定要删除${item.title}会话吗?`,
iconType: 'warning',
onOk: () => {
removeMessage(item.id).then(() => {
removeMessage(item.id).then(async () => {
message.success('删除成功')
getConversationList()
subMenuActiveIndex.value = -1
conversationDefaultShow.value = true
await getConversationList()
})
},
})
}
if (type === SubMenuActionEnum.TO_TOP) {
conversationToTop(item.id).then(async () => {
message.success('置顶成功')
await getConversationList()
const index = subMenuList.value.findIndex(item => item.id === subMenuActionData.id)
await handleSubMenuChange(index)
getHistoryMessage()
})
}
}
/**
* @description: 切换会话
*/
async function handleSubMenuChange(index: number) {
async function handleSubMenuChange(index: number, item?: SubMenuItem) {
if (messageStatus.value !== MessageStatusEnum.END) {
message.warn('请先结束对话')
return
}
if (!subMenuList.value.length) {
return
}
subMenuActiveIndex.value = index
historyMessageParams.value.current = 1
historyMessageParams.value.total = 0
messageStore.setConversationData(subMenuList.value[subMenuActiveIndex.value])
messageStore.setMessageClear()
const modelIdx = modelOptions.findIndex(v => v.value === Number(subMenuList.value[subMenuActiveIndex.value].modelType))
if (modelIdx === -1) {
message.warn('未找到该对话下的模型信息')
return
}
modelIndex.value = modelIdx
useMqtt().end()
useMqtt().connect()
getHistoryMessage()
//
appMessageShow.value = false
setTimeout(() => {
appMessageShow.value = true
}, 0)
// item
if (item) {
getHistoryMessage()
}
}
/**
@ -124,22 +149,31 @@ async function handleSubMenuChange(index: number) {
async function handleSend(value: string) {
sendBtnLoading.value = true
conversationDefaultShow.value = false
if (!conversationData.value) {
return
}
if (subMenuActiveIndex.value === -1) {
try {
await addMessage({ type: MenuTypeEnum.VISUAL_ANALYSIS, title: '新的对话' })
spinning.value = true
messageStore.setMessageClear()
await addMessage({ type: MenuTypeEnum.VISUAL_ANALYSIS, title: '新的对话', sort: subMenuList.value.length + 1, modelType: modelOptions[modelIndex.value].value })
await getConversationList()
await nextTick()
subMenuActiveIndex.value = subMenuList.value.length - 1
await handleSubMenuChange(subMenuActiveIndex.value)
if (!conversationData.value) {
message.error('对话发送,请稍后重试!')
return
}
sendMessage(conversationData.value.id, value)
spinning.value = false
}
catch (error) {
message.error('新建对话失败,请稍后重试!')
}
}
else {
if (!conversationData.value) {
return
}
sendMessage(conversationData.value.id, value)
}
}
@ -153,10 +187,6 @@ async function getConversationList() {
item.actionType = SubMenuActionEnum.NOT
})
subMenuList.value = res
subMenuActiveIndex.value = 0
if (subMenuList.value.length) {
await handleSubMenuChange(0)
}
}
/**
@ -164,18 +194,20 @@ async function getConversationList() {
*/
async function getHistoryMessage() {
if (!conversationData.value) {
spinning.value = false
conversationDefaultShow.value = true
return
}
spinning.value = true
historyMessageParams.value.conversationId = conversationData.value.id
const res = await historyMessage(historyMessageParams.value)
messageStore.setMessageClear()
spinning.value = false
if (!res || !res.records || !res.records.length) {
conversationDefaultShow.value = true
return
}
console.log(res)
console.log(res.records)
res.records.forEach((item: any) => {
const itemData: MessageItem = {
@ -190,6 +222,12 @@ async function getHistoryMessage() {
})
conversationDefaultShow.value = false
//
// appMessageShow.value = false
// setTimeout(() => {
// appMessageShow.value = true
// }, 0)
}
/**
@ -217,6 +255,10 @@ async function onScrollTop(scrollTop: number) {
* @description: 发送消息hook
*/
async function sendMessage(conversationId: string, question: string): Promise<void> {
if (!messageStore.getConversationData) {
return
}
conversationDefaultShow.value = false
messageStore.setMessageStatus(MessageStatusEnum.LOADING)
messageStore.setMessagePushItem({
@ -232,13 +274,11 @@ async function sendMessage(conversationId: string, question: string): Promise<vo
avatar: '',
messageStatus: MessageStatusEnum.LOADING,
})
if (!messageStore.getConversationData) {
return
}
sendRepository({
sendVisualAnalysis({
conversationId,
question,
fileUrl: fileList.value[0],
}).catch(() => {
messageStore.getMessageList.splice(-2)
messageStore.setMessageStatus(MessageStatusEnum.END)
@ -268,6 +308,20 @@ function handleAddMessage() {
subMenuActiveIndex.value = -1
}
/**
* @description: 点击默认看板的精选话题
*/
function handlePick(_index: number, item: TopPickItem) {
handleSend(item.label)
}
/**
* @description: gpt模型切换
*/
function handleModel(index: number) {
modelIndex.value = index
}
function handleUploadChange(info: UploadChangeParam) {
const status = info.file.status
if (status !== 'uploading') {
@ -287,11 +341,13 @@ function handleUploadDrop(e: DragEvent) {
async function handleCustomRequest(option: any) {
console.log(option)
const { file } = option
// uploadApi(file, handleUploadDrop()=>{
// console.log(111);
// })
const formData = new FormData()
formData.append('file', option.file)
uploadApi(formData).then((res) => {
fileList.value.push(res.link)
})
}
//
@ -310,11 +366,18 @@ function handleSubMenuInputBlur(index: number, item: SubMenuItem, inputValue: st
handleSubMenuInputAffirm(index, item, inputValue)
}
onMounted(() => {
getConversationList()
onMounted(async () => {
await getConversationList()
if (subMenuList.value.length) {
subMenuActiveIndex.value = 0
}
await handleSubMenuChange(subMenuActiveIndex.value)
getHistoryMessage()
})
onUnmounted(() => {
messageStore.setMessageClear()
messageStore.setConversationData(undefined)
useMqtt().end()
})
@ -331,7 +394,7 @@ onBeforeRouteLeave(() => {
<AppContainerBox>
<template #subMenu>
<!-- 标题 -->
<AppSubMenuTitle title="图像分析"></AppSubMenuTitle>
<AppSubMenuTitle></AppSubMenuTitle>
<!-- 按钮 -->
<div class="px-5 mb-5">
@ -357,11 +420,22 @@ onBeforeRouteLeave(() => {
</template>
<template #content>
<Spin :spinning="spinning" wrapper-class-name="app-content-spin">
<AppModelSelect
v-if="conversationDefaultShow"
:active-index="modelIndex"
:options="modelOptions"
@change="handleModel"
>
</AppModelSelect>
<!-- 默认导语 -->
<AppConversationDefault
v-if="conversationDefaultShow"
:is-pick="true"
:is-hot="false"
:role-list="roleList"
:apply-list="applyList"
height="calc(100% - 160px)"
:lead-data="leadData"
@handle-pick="handlePick"
>
</AppConversationDefault>
@ -370,6 +444,7 @@ onBeforeRouteLeave(() => {
v-if="!conversationDefaultShow && appMessageShow"
ref="appMessageRef"
class="pl-27 pr-5"
height="calc(100% - 160px)"
:list="messageList"
@on-scroll-top="onScrollTop"
@reload-message="reloadMessage"
@ -378,12 +453,12 @@ onBeforeRouteLeave(() => {
</Spin>
<!-- 发送框 -->
<div class="upload-box absolute right-0 bottom-5 pl-52 pr-32 mt-10s w-full">
<div v-if="!fileList.length" class="upload-box absolute right-0 bottom-5 pl-52 pr-32 mt-10s w-full">
<UploadDragger
v-model:fileList="fileList"
name="file"
:multiple="true"
:multiple="false"
:max-count="1"
:show-upload-list="false"
:custom-request="handleCustomRequest"
@change="handleUploadChange"
@drop="handleUploadDrop"
@ -397,6 +472,26 @@ onBeforeRouteLeave(() => {
</p>
</UploadDragger>
</div>
<div
v-else
class="w-full"
>
<div class="absolute left-0 bottom-35 pl-52 pr-32 mt-10 ">
已上传图片
<Image
class="rounded"
:width="60"
:height="60"
:src="fileList[0]"
fallback="data:image/png;base64,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"
></Image>
</div>
<AppTextarea
class="pl-52 pr-32 mt-10 "
:btn-loading="sendBtnLoading"
@send="handleSend"
></AppTextarea>
</div>
</template>
</AppContainerBox>
</template>

7
types/axios.d.ts vendored

@ -40,6 +40,13 @@ export interface Result<T = any> {
data: T
}
export interface UploadFileResult {
domain: string
link: string
name: string
originalName: string
}
// multipart/form-data: upload file
export interface UploadFileParams {
// Other parameters

Loading…
Cancel
Save