mirror of
https://github.com/xiaoyiweb/YiAi.git
synced 2025-11-15 05:33:45 +08:00
新增gpt-4V上传 修复部分bug
This commit is contained in:
@@ -8,12 +8,14 @@ export function fetchChatAPIProcess<T = any>(
|
||||
prompt: string
|
||||
appId?: number
|
||||
options?: { conversationId?: string; parentMessageId?: string; temperature: number }
|
||||
imageUrl?:string
|
||||
model?:string
|
||||
signal?: GenericAbortSignal
|
||||
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void },
|
||||
) {
|
||||
return post<T>({
|
||||
url: '/chatgpt/chat-process',
|
||||
data: { prompt: params.prompt, appId: params?.appId, options: params.options },
|
||||
data: { prompt: params.prompt, appId: params?.appId, options: params.options,imageUrl: params.imageUrl,model: params.model},
|
||||
signal: params.signal,
|
||||
onDownloadProgress: params.onDownloadProgress,
|
||||
})
|
||||
|
||||
BIN
chat/src/assets/file.jpeg
Normal file
BIN
chat/src/assets/file.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -230,6 +230,10 @@ onMounted(() => {
|
||||
<NInput v-model:value="loginForm.password" placeholder="请输入您的账户密码" type="password" :maxlength="30" show-password-on="click" tabindex="0" @keyup.enter="handlerSubmit" />
|
||||
</NFormItem>
|
||||
</Motion>
|
||||
<div style="color:red">
|
||||
老用户密码统一重置为112233<br>
|
||||
登录后请自行修改密码
|
||||
</div>
|
||||
<NFormItem>
|
||||
<NButton
|
||||
block
|
||||
|
||||
@@ -1,215 +1,246 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { formatChatPre, getLocalState, setLocalState } from './helper'
|
||||
import { fetchCreateGroupAPI, fetchDelAllGroupAPI, fetchDelGroupAPI, fetchQueryGroupAPI, fetchUpdateGroupAPI } from '@/api/group'
|
||||
import { fetchDelChatLogAPI, fetchDelChatLogByGroupIdAPI, fetchQueryChatLogListAPI } from '@/api/chatLog'
|
||||
import { fetchModelBaseConfigAPI } from '@/api/models'
|
||||
import { fetchGetChatPreList } from '@/api/index'
|
||||
import { defineStore } from "pinia";
|
||||
import { formatChatPre, getLocalState, setLocalState } from "./helper";
|
||||
import {
|
||||
fetchCreateGroupAPI,
|
||||
fetchDelAllGroupAPI,
|
||||
fetchDelGroupAPI,
|
||||
fetchQueryGroupAPI,
|
||||
fetchUpdateGroupAPI,
|
||||
} from "@/api/group";
|
||||
import {
|
||||
fetchDelChatLogAPI,
|
||||
fetchDelChatLogByGroupIdAPI,
|
||||
fetchQueryChatLogListAPI,
|
||||
} from "@/api/chatLog";
|
||||
import { fetchModelBaseConfigAPI } from "@/api/models";
|
||||
import { fetchGetChatPreList } from "@/api/index";
|
||||
|
||||
export const useChatStore = defineStore('chat-store', {
|
||||
state: (): Chat.ChatState => getLocalState(),
|
||||
export const useChatStore = defineStore("chat-store", {
|
||||
state: (): Chat.ChatState => getLocalState(),
|
||||
|
||||
getters: {
|
||||
/* 当前选用模型的配置 */
|
||||
activeConfig: (state) => {
|
||||
const uuid = state.active
|
||||
if (!uuid)
|
||||
return {}
|
||||
const config = state.groupList.find(item => item.uuid === uuid)?.config
|
||||
return config ? JSON.parse(config) : state.baseConfig
|
||||
},
|
||||
getters: {
|
||||
/* 当前选用模型的配置 */
|
||||
activeConfig: (state) => {
|
||||
const uuid = state.active;
|
||||
if (!uuid) return {};
|
||||
const config = state.groupList.find((item) => item.uuid === uuid)?.config;
|
||||
return config ? JSON.parse(config) : state.baseConfig;
|
||||
},
|
||||
|
||||
activeGroupAppId: (state) => {
|
||||
const uuid = state.active
|
||||
if (!uuid)
|
||||
return null
|
||||
return state.groupList.find(item => item.uuid === uuid)?.appId
|
||||
},
|
||||
activeGroupAppId: (state) => {
|
||||
const uuid = state.active;
|
||||
if (!uuid) return null;
|
||||
return state.groupList.find((item) => item.uuid === uuid)?.appId;
|
||||
},
|
||||
|
||||
/* 当前选用模型的扣费类型 */
|
||||
activeModelKeyDeductType(state) {
|
||||
return this.activeConfig?.modelInfo?.deductType
|
||||
},
|
||||
/* 当前选用模型的名称 */
|
||||
activeModelName(state) {
|
||||
return this.activeConfig?.modelInfo?.model;
|
||||
},
|
||||
/* 当前选用模型的扣费类型 */
|
||||
activeModelKeyDeductType(state) {
|
||||
return this.activeConfig?.modelInfo?.deductType;
|
||||
},
|
||||
|
||||
/* 当前选用模型的模型类型 */
|
||||
activeModelKeyType(state) {
|
||||
return this.activeConfig?.modelInfo?.keyType
|
||||
},
|
||||
/* 当前选用模型的模型类型 */
|
||||
activeModelKeyType(state) {
|
||||
return this.activeConfig?.modelInfo?.keyType;
|
||||
},
|
||||
|
||||
/* 当前选用模型的调用价格 */
|
||||
activeModelKeyPrice(state) {
|
||||
return this.activeConfig?.modelInfo?.deduct
|
||||
},
|
||||
/* 当前选用模型的调用价格 */
|
||||
activeModelKeyPrice(state) {
|
||||
return this.activeConfig?.modelInfo?.deduct;
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
actions: {
|
||||
/* 对话组过滤 */
|
||||
setGroupKeyWord(keyWord: string) {
|
||||
this.groupKeyWord = keyWord;
|
||||
},
|
||||
|
||||
actions: {
|
||||
/* 对话组过滤 */
|
||||
setGroupKeyWord(keyWord: string) {
|
||||
this.groupKeyWord = keyWord
|
||||
},
|
||||
/* 计算拿到当前选择的对话组信息 */
|
||||
getChatByGroupInfo() {
|
||||
if (this.active)
|
||||
return this.groupList.find((item) => item.uuid === this.active) || {};
|
||||
},
|
||||
|
||||
/* 计算拿到当前选择的对话组信息 */
|
||||
getChatByGroupInfo() {
|
||||
if (this.active)
|
||||
return this.groupList.find(item => item.uuid === this.active) || {}
|
||||
},
|
||||
/* */
|
||||
getConfigFromUuid(uuid: any) {
|
||||
return this.groupList.find((item) => item.uuid === uuid)?.config;
|
||||
},
|
||||
|
||||
/* */
|
||||
getConfigFromUuid(uuid: any) {
|
||||
return this.groupList.find(item => item.uuid === uuid)?.config
|
||||
},
|
||||
/* 新增新的对话组 */
|
||||
async addNewChatGroup(appId = 0) {
|
||||
const res: any = await fetchCreateGroupAPI({ appId });
|
||||
const { id: uuid } = res.data;
|
||||
await this.setActiveGroup(uuid);
|
||||
this.recordState();
|
||||
},
|
||||
|
||||
/* 新增新的对话组 */
|
||||
async addNewChatGroup(appId = 0) {
|
||||
const res: any = await fetchCreateGroupAPI({ appId })
|
||||
const { id: uuid } = res.data
|
||||
await this.setActiveGroup(uuid)
|
||||
this.recordState()
|
||||
},
|
||||
/* 查询基础模型配置 兼容老的chatgroup */
|
||||
async getBaseModelConfig() {
|
||||
const res = await fetchModelBaseConfigAPI();
|
||||
this.baseConfig = res?.data;
|
||||
},
|
||||
|
||||
/* 查询基础模型配置 兼容老的chatgroup */
|
||||
async getBaseModelConfig() {
|
||||
const res = await fetchModelBaseConfigAPI()
|
||||
this.baseConfig = res?.data
|
||||
},
|
||||
/* 查询我的对话组 */
|
||||
async queryMyGroup() {
|
||||
const res: any = await fetchQueryGroupAPI();
|
||||
this.groupList = [
|
||||
...res.data.map((item: any) => {
|
||||
const {
|
||||
id: uuid,
|
||||
title,
|
||||
isSticky,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
appId,
|
||||
config,
|
||||
appLogo,
|
||||
} = item;
|
||||
return {
|
||||
uuid,
|
||||
title,
|
||||
isEdit: false,
|
||||
appId,
|
||||
config,
|
||||
isSticky,
|
||||
appLogo,
|
||||
createdAt,
|
||||
updatedAt: new Date(updatedAt).getTime(),
|
||||
};
|
||||
}),
|
||||
];
|
||||
const isHasActive = this.groupList.some(
|
||||
(item) => Number(item.uuid) === Number(this.active)
|
||||
);
|
||||
if (!this.active || !isHasActive)
|
||||
this.groupList.length && this.setActiveGroup(this.groupList[0].uuid);
|
||||
},
|
||||
|
||||
/* 查询我的对话组 */
|
||||
async queryMyGroup() {
|
||||
const res: any = await fetchQueryGroupAPI()
|
||||
this.groupList = [...res.data.map((item: any) => {
|
||||
const { id: uuid, title, isSticky, createdAt, updatedAt, appId, config, appLogo } = item
|
||||
return { uuid, title, isEdit: false, appId, config, isSticky, appLogo, createdAt, updatedAt: new Date(updatedAt).getTime() }
|
||||
})]
|
||||
const isHasActive = this.groupList.some(item => Number(item.uuid) === Number(this.active))
|
||||
if (!this.active || !isHasActive)
|
||||
this.groupList.length && this.setActiveGroup(this.groupList[0].uuid)
|
||||
},
|
||||
/* 修改对话组信息 */
|
||||
async updateGroupInfo(params: {
|
||||
groupId: number;
|
||||
title?: string;
|
||||
isSticky?: boolean;
|
||||
}) {
|
||||
await fetchUpdateGroupAPI(params);
|
||||
},
|
||||
|
||||
/* 修改对话组信息 */
|
||||
async updateGroupInfo(params: { groupId: number; title?: string; isSticky?: boolean }) {
|
||||
await fetchUpdateGroupAPI(params)
|
||||
},
|
||||
/* 变更对话组 */
|
||||
async setActiveGroup(uuid: number) {
|
||||
this.active = uuid;
|
||||
if (this.active) await this.queryActiveChatLogList();
|
||||
else this.chatList = [];
|
||||
|
||||
/* 变更对话组 */
|
||||
async setActiveGroup(uuid: number) {
|
||||
this.active = uuid
|
||||
if (this.active)
|
||||
await this.queryActiveChatLogList()
|
||||
this.groupList.forEach((item) => (item.isEdit = false));
|
||||
this.recordState();
|
||||
},
|
||||
|
||||
else
|
||||
this.chatList = []
|
||||
/* 删除对话组 */
|
||||
async deleteGroup(params: Chat.History) {
|
||||
const curIndex = this.groupList.findIndex(
|
||||
(item) => item.uuid === params.uuid
|
||||
);
|
||||
const { uuid: groupId } = params;
|
||||
await fetchDelGroupAPI({ groupId });
|
||||
await this.queryMyGroup();
|
||||
if (this.groupList.length === 0) await this.setActiveGroup(0);
|
||||
|
||||
this.groupList.forEach(item => (item.isEdit = false))
|
||||
this.recordState()
|
||||
},
|
||||
if (curIndex > 0 && curIndex < this.groupList.length)
|
||||
await this.setActiveGroup(this.groupList[curIndex].uuid);
|
||||
|
||||
/* 删除对话组 */
|
||||
async deleteGroup(params: Chat.History) {
|
||||
const curIndex = this.groupList.findIndex(item => item.uuid === params.uuid)
|
||||
const { uuid: groupId } = params
|
||||
await fetchDelGroupAPI({ groupId })
|
||||
await this.queryMyGroup()
|
||||
if (this.groupList.length === 0)
|
||||
await this.setActiveGroup(0)
|
||||
if (curIndex === 0 && this.groupList.length > 0)
|
||||
await this.setActiveGroup(this.groupList[0].uuid);
|
||||
|
||||
if (curIndex > 0 && curIndex < this.groupList.length)
|
||||
await this.setActiveGroup(this.groupList[curIndex].uuid)
|
||||
if (
|
||||
curIndex > this.groupList.length ||
|
||||
(curIndex === 0 && this.groupList.length === 0)
|
||||
)
|
||||
await this.setActiveGroup(0);
|
||||
|
||||
if (curIndex === 0 && this.groupList.length > 0)
|
||||
await this.setActiveGroup(this.groupList[0].uuid)
|
||||
if (curIndex > 0 && curIndex === this.groupList.length)
|
||||
await this.setActiveGroup(this.groupList[curIndex - 1].uuid);
|
||||
|
||||
if (curIndex > this.groupList.length || (curIndex === 0 && this.groupList.length === 0))
|
||||
await this.setActiveGroup(0)
|
||||
this.recordState();
|
||||
},
|
||||
|
||||
if (curIndex > 0 && curIndex === this.groupList.length)
|
||||
await this.setActiveGroup(this.groupList[curIndex - 1].uuid)
|
||||
/* 删除全部非置顶对话组 */
|
||||
async delAllGroup() {
|
||||
if (!this.active || !this.groupList.length) return;
|
||||
await fetchDelAllGroupAPI();
|
||||
await this.queryMyGroup();
|
||||
if (this.groupList.length === 0) await this.setActiveGroup(0);
|
||||
else await this.setActiveGroup(this.groupList[0].uuid);
|
||||
},
|
||||
|
||||
this.recordState()
|
||||
},
|
||||
/* 查询当前对话组的聊天记录 */
|
||||
async queryActiveChatLogList() {
|
||||
if (!this.active || Number(this.active) === 0) return;
|
||||
const res: any = await fetchQueryChatLogListAPI({ groupId: this.active });
|
||||
this.chatList = res.data;
|
||||
},
|
||||
|
||||
/* 删除全部非置顶对话组 */
|
||||
async delAllGroup() {
|
||||
if (!this.active || !this.groupList.length)
|
||||
return
|
||||
await fetchDelAllGroupAPI()
|
||||
await this.queryMyGroup()
|
||||
if (this.groupList.length === 0)
|
||||
await this.setActiveGroup(0)
|
||||
/* 添加一条虚拟的对话记录 */
|
||||
addGroupChat(data) {
|
||||
this.chatList = [...this.chatList, data];
|
||||
},
|
||||
|
||||
else
|
||||
await this.setActiveGroup(this.groupList[0].uuid)
|
||||
},
|
||||
/* 动态修改对话记录 */
|
||||
updateGroupChat(index: number, data: Chat.Chat) {
|
||||
this.chatList[index] = { ...this.chatList[index], ...data };
|
||||
},
|
||||
|
||||
/* 查询当前对话组的聊天记录 */
|
||||
async queryActiveChatLogList() {
|
||||
if (!this.active || Number(this.active) === 0)
|
||||
return
|
||||
const res: any = await fetchQueryChatLogListAPI({ groupId: this.active })
|
||||
this.chatList = res.data
|
||||
},
|
||||
/* 修改其中部分内容 */
|
||||
updateGroupChatSome(index: number, data: Partial<Chat.Chat>) {
|
||||
this.chatList[index] = { ...this.chatList[index], ...data };
|
||||
},
|
||||
|
||||
/* 添加一条虚拟的对话记录 */
|
||||
addGroupChat(data) {
|
||||
this.chatList = [...this.chatList, data]
|
||||
},
|
||||
/* 删除一条对话记录 */
|
||||
async deleteChatById(chatId: number | undefined) {
|
||||
console.log(chatId);
|
||||
if (!chatId) return;
|
||||
await fetchDelChatLogAPI({ id: chatId });
|
||||
await this.queryActiveChatLogList();
|
||||
},
|
||||
|
||||
/* 动态修改对话记录 */
|
||||
updateGroupChat(index: number, data: Chat.Chat) {
|
||||
this.chatList[index] = { ...this.chatList[index], ...data }
|
||||
},
|
||||
/* 查询快问预设 */
|
||||
async queryChatPre() {
|
||||
const res: any = await fetchGetChatPreList();
|
||||
if (!res.data) return;
|
||||
this.chatPreList = formatChatPre(res.data);
|
||||
},
|
||||
|
||||
/* 修改其中部分内容 */
|
||||
updateGroupChatSome(index: number, data: Partial<Chat.Chat>) {
|
||||
this.chatList[index] = { ...this.chatList[index], ...data }
|
||||
},
|
||||
/* 设置使用上下文 */
|
||||
setUsingContext(context: boolean) {
|
||||
this.usingContext = context;
|
||||
this.recordState();
|
||||
},
|
||||
|
||||
/* 删除一条对话记录 */
|
||||
async deleteChatById(chatId: number | undefined) {
|
||||
console.log(chatId)
|
||||
if (!chatId)
|
||||
return
|
||||
await fetchDelChatLogAPI({ id: chatId })
|
||||
await this.queryActiveChatLogList()
|
||||
},
|
||||
/* 设置使用联网 */
|
||||
setUsingNetwork(context: boolean) {
|
||||
this.usingNetwork = context;
|
||||
this.recordState();
|
||||
},
|
||||
|
||||
/* 查询快问预设 */
|
||||
async queryChatPre() {
|
||||
const res: any = await fetchGetChatPreList()
|
||||
if (!res.data)
|
||||
return
|
||||
this.chatPreList = formatChatPre(res.data)
|
||||
},
|
||||
/* 删除当前对话组的全部内容 */
|
||||
async clearChatByGroupId() {
|
||||
if (!this.active) return;
|
||||
|
||||
/* 设置使用上下文 */
|
||||
setUsingContext(context: boolean) {
|
||||
this.usingContext = context
|
||||
this.recordState()
|
||||
},
|
||||
await fetchDelChatLogByGroupIdAPI({ groupId: this.active });
|
||||
await this.queryActiveChatLogList();
|
||||
},
|
||||
|
||||
/* 设置使用联网 */
|
||||
setUsingNetwork(context: boolean) {
|
||||
this.usingNetwork = context
|
||||
this.recordState()
|
||||
},
|
||||
recordState() {
|
||||
setLocalState(this.$state);
|
||||
},
|
||||
|
||||
/* 删除当前对话组的全部内容 */
|
||||
async clearChatByGroupId() {
|
||||
if (!this.active)
|
||||
return
|
||||
|
||||
await fetchDelChatLogByGroupIdAPI({ groupId: this.active })
|
||||
await this.queryActiveChatLogList()
|
||||
},
|
||||
|
||||
recordState() {
|
||||
setLocalState(this.$state)
|
||||
},
|
||||
|
||||
clearChat() {
|
||||
this.chatList = []
|
||||
this.groupList = []
|
||||
this.active = 0
|
||||
this.recordState()
|
||||
},
|
||||
},
|
||||
})
|
||||
clearChat() {
|
||||
this.chatList = [];
|
||||
this.groupList = [];
|
||||
this.active = 0;
|
||||
this.recordState();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -12,7 +12,9 @@ import {
|
||||
NTooltip,
|
||||
useDialog,
|
||||
useMessage,
|
||||
NAlert
|
||||
} from 'naive-ui'
|
||||
import type { MessageRenderMessage } from 'naive-ui'
|
||||
|
||||
import html2canvas from 'html2canvas'
|
||||
import { useRoute } from 'vue-router'
|
||||
@@ -86,7 +88,7 @@ const theme = computed(() => appStore.theme)
|
||||
|
||||
const globaelConfig = computed(() => authStore.globalConfig)
|
||||
const isSetBeian = computed(
|
||||
() => globaelConfig.value?.companyName && globaelConfig.value?.filingNumber,
|
||||
() => globaelConfig.value?.companyName && globaelConfig.value?.filingNumber
|
||||
)
|
||||
const { addGroupChat, updateGroupChat, updateGroupChatSome } = useChat()
|
||||
const tradeStatus = computed(() => route.query.trade_status as string)
|
||||
@@ -101,13 +103,13 @@ const dataSources = computed(() => chatStore.chatList)
|
||||
|
||||
/* 当前所有的ai回复信息列表 方便拿到上下文 */
|
||||
const conversationList = computed(() =>
|
||||
dataSources.value.filter(item => !item.inversion && !item.error),
|
||||
dataSources.value.filter((item) => !item.inversion && !item.error)
|
||||
)
|
||||
|
||||
/* 当前上下文有id的最后一条 防止停止回答的时候 上一条的id是空 接不上上下文 */
|
||||
const lastContext = computed(() => {
|
||||
const hasIdCoversationList = conversationList.value.filter(
|
||||
item => item.conversationOptions?.parentMessageId,
|
||||
(item) => item.conversationOptions?.parentMessageId
|
||||
)
|
||||
return hasIdCoversationList[hasIdCoversationList.length - 1]
|
||||
?.conversationOptions
|
||||
@@ -120,17 +122,22 @@ const firstScroll = ref<boolean>(true)
|
||||
const tipsRef = ref<any>(null)
|
||||
const tipText = ref('')
|
||||
const tipsHeight = ref<any>(null)
|
||||
const dataBase64 = ref(null)
|
||||
const fileName = ref('')
|
||||
const isImageFile = ref(false)
|
||||
const showDeleteIcon = ref(false)
|
||||
|
||||
/* 当前选中的对话组 */
|
||||
const activeGroupId = computed(() => chatStore.active)
|
||||
/* 当前对话组的详细信息 */
|
||||
const activeGroupInfo = computed(() =>
|
||||
chatStore.groupList.find((item: any) => item.uuid === chatStore.active),
|
||||
chatStore.groupList.find((item: any) => item.uuid === chatStore.active)
|
||||
)
|
||||
/* 当前选用的模型的类型 1: openai 2: 百度 */
|
||||
const activeModelKeyType = computed(() => Number(chatStore?.activeModelKeyType))
|
||||
/* 当前对话组是否是应用 */
|
||||
const activeAppId = computed(() =>
|
||||
activeGroupInfo?.value ? activeGroupInfo.value.appId : 0,
|
||||
activeGroupInfo?.value ? activeGroupInfo.value.appId : 0
|
||||
)
|
||||
/* 粘贴板的文字 */
|
||||
const clipboardText = computed(() => useGlobalStore.clipboardText)
|
||||
@@ -144,43 +151,37 @@ watch(clipboardText, (val) => {
|
||||
watch(
|
||||
activeAppId,
|
||||
(val) => {
|
||||
if (val)
|
||||
queryAppDetail(val)
|
||||
if (val) queryAppDetail(val)
|
||||
else appDetail.value = null
|
||||
},
|
||||
{ immediate: true },
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
activeGroupId,
|
||||
(val) => {
|
||||
if (val)
|
||||
firstScroll.value = true
|
||||
if (inputRef.value && !isMobile.value)
|
||||
inputRef.value?.focus()
|
||||
if (val) firstScroll.value = true
|
||||
if (inputRef.value && !isMobile.value) inputRef.value?.focus()
|
||||
},
|
||||
{ immediate: true },
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
dataSources,
|
||||
(val) => {
|
||||
if (val.length === 0)
|
||||
return
|
||||
if (val.length === 0) return
|
||||
if (firstScroll.value) {
|
||||
firstScroll.value = false
|
||||
scrollToBottom()
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const modelName = computed(() => {
|
||||
if (!chatStore.activeConfig)
|
||||
return
|
||||
if (!chatStore.activeConfig) return
|
||||
const { modelTypeInfo, modelInfo } = chatStore.activeConfig
|
||||
if (!modelTypeInfo || !modelInfo)
|
||||
return
|
||||
if (!modelTypeInfo || !modelInfo) return
|
||||
return `${modelInfo.modelName}`
|
||||
})
|
||||
function handleOpenModelDialog() {
|
||||
@@ -202,14 +203,52 @@ let curFile: File | null
|
||||
|
||||
async function handleFileSelect(event: any) {
|
||||
const file = event?.target?.files[0]
|
||||
if (file.size <= 5 * 1024 * 1024)
|
||||
if (!file) return
|
||||
if (file.size <= 10 * 1024 * 1024) {
|
||||
await handleSetFile(file)
|
||||
else ms.error('上传文件失败,上传大小不能超过5M')
|
||||
} else {
|
||||
return ms.error('上传文件失败,上传大小不能超过10M')
|
||||
}
|
||||
let trimmedFileName = file.name
|
||||
const maxLength = 8 // 最大长度限制
|
||||
const extension = trimmedFileName.split('.').pop() // 获取文件扩展名
|
||||
|
||||
if (trimmedFileName.length > maxLength) {
|
||||
// 截取文件名并添加省略号,同时保留扩展名
|
||||
trimmedFileName =
|
||||
trimmedFileName.substring(0, maxLength - extension.length - 1) +
|
||||
'….' +
|
||||
extension
|
||||
}
|
||||
|
||||
fileName.value = trimmedFileName // 更新文件名
|
||||
console.log(file.type)
|
||||
// 检查文件类型
|
||||
if (file.type.startsWith('image/')) {
|
||||
// 处理图像文件
|
||||
isImageFile.value = true
|
||||
handleSetFile(file)
|
||||
} else if (
|
||||
file.type.startsWith('application/') ||
|
||||
file.type.startsWith('text/')
|
||||
) {
|
||||
// 处理文件类型
|
||||
isImageFile.value = false
|
||||
handleSetFile(file)
|
||||
} else {
|
||||
// 处理其他类型的文件或显示错误消息
|
||||
ms.error('上传文件失败,不支持此类型文件')
|
||||
console.log('不支持的文件类型')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSetFile(file: File) {
|
||||
curFile = file
|
||||
uploadFile()
|
||||
const reader = new FileReader()
|
||||
reader.onload = (event: any) => {
|
||||
dataBase64.value = event.target?.result as string
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
function uploadBtn() {
|
||||
@@ -233,11 +272,13 @@ async function uploadFile() {
|
||||
}
|
||||
},
|
||||
})
|
||||
prompt.value = `请分析一下上传的这个文件:${res?.data?.data}`
|
||||
ms.success('上传成功,输入内容后发送', { duration: 5000, closable: true })
|
||||
}
|
||||
catch (error) {
|
||||
ms.error('网络异常,上传失败')
|
||||
return res?.data?.data
|
||||
} catch (error) {
|
||||
ms.error('网络异常,发送失败')
|
||||
return null
|
||||
} finally {
|
||||
dataBase64.value = null
|
||||
curFile = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,9 +312,9 @@ function handleScrollBtm() {
|
||||
/* 发送消息 */
|
||||
async function handleSubmit(index?: number) {
|
||||
if (
|
||||
chatStore.groupList.length === 0
|
||||
|| loading.value
|
||||
|| !typingStatusEnd.value
|
||||
chatStore.groupList.length === 0 ||
|
||||
loading.value ||
|
||||
!typingStatusEnd.value
|
||||
)
|
||||
return
|
||||
let message = ''
|
||||
@@ -287,12 +328,10 @@ async function handleSubmit(index?: number) {
|
||||
|
||||
function parseTextToJSON(input: string) {
|
||||
const startIndex = input.indexOf(',"text":"') + 10
|
||||
if (startIndex === -1)
|
||||
return { text: '' }
|
||||
if (startIndex === -1) return { text: '' }
|
||||
|
||||
let endIndex = input.indexOf('","delta"', startIndex)
|
||||
if (endIndex === -1)
|
||||
endIndex = input.length - 1
|
||||
if (endIndex === -1) endIndex = input.length - 1
|
||||
else endIndex = endIndex - 10
|
||||
|
||||
const text = input.substring(startIndex, endIndex)
|
||||
@@ -301,15 +340,17 @@ function parseTextToJSON(input: string) {
|
||||
|
||||
/* 按钮发送消息 */
|
||||
async function onConversation(msg?: string) {
|
||||
let imageUrl = null
|
||||
if (dataBase64.value || curFile) {
|
||||
imageUrl = await uploadFile()
|
||||
}
|
||||
let message = msg || prompt.value
|
||||
if (tipText.value && !message.includes(tipText.value))
|
||||
message = `${tipText.value}\n${message}`
|
||||
|
||||
if (loading.value)
|
||||
return
|
||||
if (loading.value) return
|
||||
|
||||
if (!message || message.trim() === '')
|
||||
return
|
||||
if (!message || message.trim() === '') return
|
||||
|
||||
controller = new AbortController()
|
||||
|
||||
@@ -319,6 +360,7 @@ async function onConversation(msg?: string) {
|
||||
text: message,
|
||||
inversion: true,
|
||||
error: false,
|
||||
imageUrl,
|
||||
conversationOptions: null,
|
||||
requestOptions: { prompt: message, options: null },
|
||||
})
|
||||
@@ -369,12 +411,10 @@ async function onConversation(msg?: string) {
|
||||
if (cacheResText.length - i > 150) {
|
||||
currentText += cacheResText.substring(i, i + 10)
|
||||
i += 10
|
||||
}
|
||||
else if (cacheResText.length - i > 200) {
|
||||
} else if (cacheResText.length - i > 200) {
|
||||
currentText += cacheResText.substring(i)
|
||||
i += cacheResText.length - i
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
currentText += cacheResText[i]
|
||||
i++
|
||||
}
|
||||
@@ -396,8 +436,8 @@ async function onConversation(msg?: string) {
|
||||
const curLen = currentText ? currentText.length : 0
|
||||
const cacheResLen = cacheResText ? cacheResText.length : 0
|
||||
if (
|
||||
!isStreamIn.value
|
||||
&& (curLen === cacheResLen || curLen > cacheResLen)
|
||||
!isStreamIn.value &&
|
||||
(curLen === cacheResLen || curLen > cacheResLen)
|
||||
) {
|
||||
typingStatusEnd.value = true
|
||||
updateGroupChatSome(dataSources.value.length - 1, {
|
||||
@@ -413,12 +453,12 @@ async function onConversation(msg?: string) {
|
||||
authStore.updateUserBanance(userBanance)
|
||||
|
||||
if (
|
||||
dataSources.value.length === 2
|
||||
&& !activeGroupInfo?.value?.appId
|
||||
dataSources.value.length === 2 &&
|
||||
!activeGroupInfo?.value?.appId
|
||||
) {
|
||||
const lengthStr = isMobile.value ? 10 : 20
|
||||
const title
|
||||
= dataSources.value[1].text.length > lengthStr
|
||||
const title =
|
||||
dataSources.value[1].text.length > lengthStr
|
||||
? dataSources.value[1].text.slice(0, lengthStr)
|
||||
: dataSources.value[1].text
|
||||
chatStore
|
||||
@@ -433,8 +473,7 @@ async function onConversation(msg?: string) {
|
||||
/* 有多余的再请求下一帧 */
|
||||
if (cacheResText.length && cacheResText.length > currentText.length) {
|
||||
requestAnimationFrame(update)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
requestAnimationFrame(update)
|
||||
}, 1000)
|
||||
@@ -447,6 +486,8 @@ async function onConversation(msg?: string) {
|
||||
prompt: message,
|
||||
appId: activeGroupInfo.value ? activeGroupInfo.value.appId : 0,
|
||||
options,
|
||||
imageUrl,
|
||||
model: chatStore?.activeModelName,
|
||||
signal: controller.signal,
|
||||
onDownloadProgress: ({ event }) => {
|
||||
const xhr = event.target
|
||||
@@ -456,16 +497,14 @@ async function onConversation(msg?: string) {
|
||||
if ([1].includes(activeModelKeyType.value)) {
|
||||
const lastIndex = responseText.lastIndexOf(
|
||||
'\n',
|
||||
responseText.length - 2,
|
||||
responseText.length - 2
|
||||
)
|
||||
let chunk = responseText
|
||||
if (lastIndex !== -1)
|
||||
chunk = responseText.substring(lastIndex)
|
||||
if (lastIndex !== -1) chunk = responseText.substring(lastIndex)
|
||||
|
||||
try {
|
||||
data = JSON.parse(chunk)
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
/* 二次解析 */
|
||||
// const parseData = parseTextToJSON(responseText)
|
||||
// TODO 如果出现类似超时错误 会连接上次的内容一起发出来导致无法解析 后端需要处理 下
|
||||
@@ -489,8 +528,7 @@ async function onConversation(msg?: string) {
|
||||
const parseData = JSON.parse(line)
|
||||
cacheResult += parseData.result
|
||||
tem = parseData
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
console.log('Json parse 2 3 type error: ')
|
||||
}
|
||||
}
|
||||
@@ -502,8 +540,7 @@ async function onConversation(msg?: string) {
|
||||
/* 如果出现输出内容不一致就需要处理了 */
|
||||
if (activeModelKeyType.value === 1) {
|
||||
cacheResText = data.text
|
||||
if (data?.userBanance)
|
||||
userBanance = data?.userBanance
|
||||
if (data?.userBanance) userBanance = data?.userBanance
|
||||
}
|
||||
|
||||
if ([2, 3].includes(activeModelKeyType.value)) {
|
||||
@@ -512,24 +549,21 @@ async function onConversation(msg?: string) {
|
||||
isStreamIn.value = !is_end
|
||||
data?.userBanance && (userBanance = data?.userBanance)
|
||||
}
|
||||
}
|
||||
catch (error) {}
|
||||
} catch (error) {}
|
||||
},
|
||||
})
|
||||
}
|
||||
await fetchChatAPIOnce()
|
||||
}
|
||||
catch (error: any) {
|
||||
} catch (error: any) {
|
||||
useGlobalStore.updateIsChatIn(false)
|
||||
clearInterval(timer)
|
||||
isStreamIn.value = false
|
||||
if (
|
||||
error.code === 402
|
||||
|| error?.message.includes('余额不足')
|
||||
|| error?.message.includes('免费额度已经使用完毕')
|
||||
error.code === 402 ||
|
||||
error?.message.includes('余额不足') ||
|
||||
error?.message.includes('免费额度已经使用完毕')
|
||||
) {
|
||||
if (isLogin.value)
|
||||
useGlobalStore.updateGoodsDialog(true)
|
||||
if (isLogin.value) useGlobalStore.updateGoodsDialog(true)
|
||||
else authStore.setLoginDialog(true)
|
||||
}
|
||||
|
||||
@@ -568,10 +602,10 @@ async function onConversation(msg?: string) {
|
||||
requestOptions: { prompt: message, options: { ...options } },
|
||||
})
|
||||
scrollToBottomIfAtBottom()
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
loading.value = false
|
||||
isStreamIn.value = false
|
||||
imageUrl = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,16 +623,14 @@ function handleRefresh() {
|
||||
ms.success('感谢你的购买、祝您使用愉快~', { duration: 5000 })
|
||||
authStore.getUserInfo()
|
||||
router.replace({ name: 'Chat', query: {} })
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ms.error('您还没有购买成功哦~')
|
||||
}
|
||||
}
|
||||
|
||||
/* 导出 */
|
||||
function handleExport() {
|
||||
if (loading.value)
|
||||
return
|
||||
if (loading.value) return
|
||||
|
||||
const d = dialog.warning({
|
||||
title: t('chat.exportImage'),
|
||||
@@ -627,11 +659,9 @@ function handleExport() {
|
||||
d.loading = false
|
||||
ms.success(t('chat.exportSuccess'))
|
||||
Promise.resolve()
|
||||
}
|
||||
catch (error: any) {
|
||||
} catch (error: any) {
|
||||
ms.error(t('chat.exportFailed'))
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
d.loading = false
|
||||
}
|
||||
},
|
||||
@@ -640,8 +670,7 @@ function handleExport() {
|
||||
|
||||
/* 删除 */
|
||||
function handleDelete({ chatId }: Chat.Chat) {
|
||||
if (loading.value)
|
||||
return
|
||||
if (loading.value) return
|
||||
|
||||
dialog.warning({
|
||||
title: t('chat.deleteMessage'),
|
||||
@@ -655,8 +684,7 @@ function handleDelete({ chatId }: Chat.Chat) {
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
if (loading.value)
|
||||
return
|
||||
if (loading.value) return
|
||||
|
||||
dialog.warning({
|
||||
title: t('chat.clearChat'),
|
||||
@@ -676,8 +704,7 @@ function handleEnter(event: KeyboardEvent) {
|
||||
event.preventDefault()
|
||||
handleSubmit()
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (event.key === 'Enter' && event.ctrlKey) {
|
||||
event.preventDefault()
|
||||
handleSubmit()
|
||||
@@ -695,17 +722,16 @@ function handleStop() {
|
||||
}
|
||||
|
||||
const placeholder = computed(() => {
|
||||
if (isMobile.value)
|
||||
return t('chat.placeholderMobile')
|
||||
if (isMobile.value) return t('chat.placeholderMobile')
|
||||
return t('chat.placeholder')
|
||||
})
|
||||
|
||||
const buttonDisabled = computed(() => {
|
||||
return (
|
||||
loading.value
|
||||
|| !prompt.value
|
||||
|| prompt.value.trim() === ''
|
||||
|| !typingStatusEnd.value
|
||||
loading.value ||
|
||||
!prompt.value ||
|
||||
prompt.value.trim() === '' ||
|
||||
!typingStatusEnd.value
|
||||
)
|
||||
})
|
||||
// 改动:发送后添加loading圈圈
|
||||
@@ -719,8 +745,7 @@ function getTipsRefHeight() {
|
||||
|
||||
function onInputeTip() {
|
||||
tipsHeight.value = 'auto'
|
||||
if (!tipText.value)
|
||||
tipsHeight.value = 0
|
||||
if (!tipText.value) tipsHeight.value = 0
|
||||
|
||||
nextTick(() => getTipsRefHeight())
|
||||
}
|
||||
@@ -728,24 +753,20 @@ function onInputeTip() {
|
||||
onMounted(async () => {
|
||||
chatStore.queryChatPre()
|
||||
|
||||
if (token.value)
|
||||
otherLoginByToken(token.value)
|
||||
if (token.value) otherLoginByToken(token.value)
|
||||
|
||||
if (tradeStatus.value)
|
||||
handleRefresh()
|
||||
if (tradeStatus.value) handleRefresh()
|
||||
|
||||
nextTick(async () => {
|
||||
await chatStore.queryActiveChatLogList()
|
||||
scrollToBottom()
|
||||
if (inputRef.value && !isMobile.value)
|
||||
inputRef.value?.focus()
|
||||
if (inputRef.value && !isMobile.value) inputRef.value?.focus()
|
||||
})
|
||||
})
|
||||
const darkMode = computed(() => appStore.theme === 'dark')
|
||||
|
||||
onUnmounted(() => {
|
||||
if (loading.value)
|
||||
controller.abort()
|
||||
if (loading.value) controller.abort()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -793,6 +814,7 @@ onUnmounted(() => {
|
||||
:inversion="item.inversion"
|
||||
:error="item.error"
|
||||
:loading="item.loading"
|
||||
:imageUrl="item.imageUrl"
|
||||
@regenerate="handleSubmit(index)"
|
||||
@delete="handleDelete(item)"
|
||||
/>
|
||||
@@ -825,10 +847,10 @@ onUnmounted(() => {
|
||||
'text-[#3076fd]': usingContext,
|
||||
'text-[#a8071a]': !usingContext,
|
||||
}"
|
||||
><SvgIcon
|
||||
class="text-lg"
|
||||
style="width: 1em; height: 1em"
|
||||
icon="ri:chat-history-line"
|
||||
><SvgIcon
|
||||
class="text-lg"
|
||||
style="width: 1em; height: 1em"
|
||||
icon="ri:chat-history-line"
|
||||
/></span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -852,10 +874,11 @@ onUnmounted(() => {
|
||||
class="shrink0 flex h-8 w-8 items-center justify-center rounded border transition hover:bg-[#eef0f3] dark:border-neutral-700 dark:hover:bg-[#33373c]"
|
||||
@click="openChatPre"
|
||||
>
|
||||
<span><SvgIcon
|
||||
class="text-lg"
|
||||
style="width: 1em; height: 1em"
|
||||
icon="noto:open-book"
|
||||
<span
|
||||
><SvgIcon
|
||||
class="text-lg"
|
||||
style="width: 1em; height: 1em"
|
||||
icon="noto:open-book"
|
||||
/></span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -950,7 +973,7 @@ onUnmounted(() => {
|
||||
<template #icon>
|
||||
<span class="text-base text-slate-500 dark:text-slate-400">
|
||||
<!-- <SvgIcon icon="streamline-emojis:wrapped-gift-1" /> -->
|
||||
<img :src="modelSvg" class="h-8" alt="">
|
||||
<img :src="modelSvg" class="h-8" alt="" />
|
||||
</span>
|
||||
</template>
|
||||
<span style="color: #3076fd">{{ modelName }}</span>
|
||||
@@ -1007,7 +1030,10 @@ onUnmounted(() => {
|
||||
<div class="flex space-x-2">
|
||||
<NTooltip
|
||||
v-if="
|
||||
chatStore.activeConfig.modelInfo.model === 'gpt-4-all'
|
||||
!dataBase64 &&
|
||||
(chatStore.activeConfig.modelInfo.model === 'gpt-4-all' ||
|
||||
chatStore.activeConfig.modelInfo.model ===
|
||||
'gpt-4-vision-preview')
|
||||
"
|
||||
trigger="hover"
|
||||
placement="bottom-end"
|
||||
@@ -1025,24 +1051,70 @@ onUnmounted(() => {
|
||||
:multiple="false"
|
||||
type="file"
|
||||
style="display: none"
|
||||
accept="text/plain,image/*, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf"
|
||||
@change="handleFileSelect($event)"
|
||||
>
|
||||
<SvgIcon
|
||||
class="text-lg"
|
||||
style="width: 1em; height: 1em"
|
||||
icon="mingcute:upload-line"
|
||||
/></span>
|
||||
:accept="
|
||||
chatStore.activeConfig.modelInfo.model ===
|
||||
'gpt-4-vision-preview'
|
||||
? 'image/*'
|
||||
: 'text/plain,image/*, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/pdf'
|
||||
"
|
||||
@change="handleFileSelect($event)" />
|
||||
<SvgIcon icon="mingcute:upload-line"
|
||||
/></span>
|
||||
</button>
|
||||
</template>
|
||||
上传
|
||||
</NTooltip>
|
||||
<!-- 预览容器 -->
|
||||
<div
|
||||
v-if="dataBase64"
|
||||
class="relative flex items-start justify-start"
|
||||
>
|
||||
<div
|
||||
class="group"
|
||||
@mouseover="showDeleteIcon = true"
|
||||
@mouseleave="showDeleteIcon = false"
|
||||
>
|
||||
<!-- 根据 isImageFile 的值显示不同内容 -->
|
||||
<template v-if="isImageFile">
|
||||
<!-- 图片预览 -->
|
||||
<img
|
||||
:src="dataBase64"
|
||||
class="max-w-full max-h-10 border border-gray-300 rounded-lg"
|
||||
alt="预览图片"
|
||||
/>
|
||||
<!-- 清除图标 -->
|
||||
<SvgIcon
|
||||
class="close-icon"
|
||||
icon="gg:close-o"
|
||||
@click="dataBase64 = ''"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- 非图片文件预览(例如文件图标) -->
|
||||
<div
|
||||
style="white-space: nowrap; padding: 0.25rem"
|
||||
class="flex items-center justify-center border border-gray-300 rounded-lg h-8 hover:bg-gray-100 text-gray-700 dark:hover:bg-gray-700 dark:text-gray-400"
|
||||
>
|
||||
<span>{{ fileName }}</span>
|
||||
<!-- 清除图标 -->
|
||||
<SvgIcon
|
||||
class="close-icon"
|
||||
icon="gg:close-o"
|
||||
@click="dataBase64 = ''"
|
||||
/>
|
||||
<!-- 替换为适当的文件图标 -->
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div
|
||||
class="flex items-center text-neutral-400 cursor-pointer hover:text-[#3076fd]"
|
||||
>
|
||||
<span class="ml-2 mr-2 text-xs" @click="toggleUsingNetwork">{{ usingNetwork ? '关闭' : '开启' }}联网访问</span>
|
||||
<span class="ml-2 mr-2 text-xs" @click="toggleUsingNetwork"
|
||||
>{{ usingNetwork ? '关闭' : '开启' }}联网访问</span
|
||||
>
|
||||
<NTooltip trigger="hover" :disabled="isMobile">
|
||||
<template #trigger>
|
||||
<SvgIcon
|
||||
@@ -1101,9 +1173,10 @@ onUnmounted(() => {
|
||||
class="ml-2 transition-all text-[#aeaeae] hover:text-[#60606d]"
|
||||
href="https://beian.miit.gov.cn"
|
||||
target="_blank"
|
||||
>{{ globaelConfig?.filingNumber }}</a>
|
||||
>{{ globaelConfig?.filingNumber }}</a
|
||||
>
|
||||
</div>
|
||||
<NModal v-model:show="showProgressModal" :mask-closable="false">
|
||||
<!-- <NModal v-model:show="showProgressModal" :mask-closable="false">
|
||||
<NCard
|
||||
style="width: 80%"
|
||||
title="上传文件中"
|
||||
@@ -1119,7 +1192,7 @@ onUnmounted(() => {
|
||||
processing
|
||||
/>
|
||||
</NCard>
|
||||
</NModal>
|
||||
</NModal> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1137,4 +1210,31 @@ onUnmounted(() => {
|
||||
.shrink0 {
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
top: -0;
|
||||
right: 0;
|
||||
color: #ff6347;
|
||||
font-size: 1rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
animation: scaleAnim 2s infinite ease-in-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
font-weight: 800;
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scaleAnim {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,6 +15,7 @@ interface Props {
|
||||
text?: string
|
||||
loading?: boolean
|
||||
asRawText?: boolean
|
||||
imageUrl?: string
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
@@ -83,6 +84,12 @@ const text = computed(() => {
|
||||
if (!props.asRawText) return mdi.render(value)
|
||||
return value
|
||||
})
|
||||
const imageUrl = computed(() => props.imageUrl)
|
||||
|
||||
const isImageUrl = computed(() => {
|
||||
if (!imageUrl.value) return false
|
||||
return /\.(jpg|jpeg|png|gif)$/i.test(imageUrl.value)
|
||||
})
|
||||
|
||||
function highlightBlock(str: string, lang?: string) {
|
||||
return `<pre class="code-block-wrapper ${
|
||||
@@ -122,7 +129,11 @@ defineExpose({ textRef })
|
||||
v-html="text"
|
||||
/>
|
||||
<div v-else class="w-full whitespace-pre-wrap" v-text="text" />
|
||||
<span v-if="loading" class="dark:text-white w-[4px] h-[20px] block animate-blink" style="display:none"/>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="dark:text-white w-[4px] h-[20px] block animate-blink"
|
||||
style="display: none"
|
||||
/>
|
||||
</div>
|
||||
<!-- 小易改动:注册掉底部的内容 -->
|
||||
<!-- <div style="margin-top: 0.5rem"> -->
|
||||
@@ -170,6 +181,28 @@ defineExpose({ textRef })
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="whitespace-pre-wrap" v-text="text" />
|
||||
<a v-if="imageUrl && isImageUrl" :href="imageUrl" target="_blank">
|
||||
<img
|
||||
:src="imageUrl"
|
||||
alt="图片"
|
||||
class="h-auto rounded-md mb-1"
|
||||
:class="{ 'max-w-full': isMobile, 'max-w-sm': !isMobile }"
|
||||
style="margin-top: 0.5rem"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
:href="imageUrl"
|
||||
target="_blank"
|
||||
:class="{ 'file-2': isMobile, 'file-1': !isMobile }"
|
||||
>
|
||||
<img
|
||||
src="@/assets/file.jpeg"
|
||||
alt="文件"
|
||||
class="h-auto rounded-md mb-1"
|
||||
:class="{ 'file-2': isMobile, 'file-1': !isMobile }"
|
||||
v-if="imageUrl && !isImageUrl"
|
||||
/>
|
||||
</a>
|
||||
<div v-if="false" style="margin-left: 0.5rem">
|
||||
<NButton class="ml-2" text color="#FFF" @click="handleCopy">
|
||||
<template #icon>
|
||||
@@ -251,4 +284,17 @@ defineExpose({ textRef })
|
||||
html.dark pre code.hljs {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.file-1 {
|
||||
display: inline;
|
||||
margin-top: 0.5rem;
|
||||
width: 120px;
|
||||
height: 150px;
|
||||
}
|
||||
.file-2 {
|
||||
display: inline;
|
||||
margin-top: 0.5rem;
|
||||
width: 90px;
|
||||
height: 120px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,6 +15,7 @@ interface Props {
|
||||
inversion?: boolean
|
||||
error?: boolean
|
||||
loading?: boolean
|
||||
imageUrl?: string
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
@@ -53,7 +54,9 @@ const options = computed(() => {
|
||||
common.unshift({
|
||||
label: asRawText.value ? t('chat.preview') : t('chat.showRawText'),
|
||||
key: 'toggleRenderType',
|
||||
icon: iconRender({ icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code' }),
|
||||
icon: iconRender({
|
||||
icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code',
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,8 +104,14 @@ function handleRegenerate() {
|
||||
>
|
||||
<AvatarComponent :image="inversion" />
|
||||
</div>
|
||||
<div class="overflow-hidden text-sm " :class="[inversion ? 'items-end' : 'items-start']">
|
||||
<p class="text-xs text-[#b4bbc4]" :class="[inversion ? 'text-right' : 'text-left']">
|
||||
<div
|
||||
class="overflow-hidden text-sm"
|
||||
:class="[inversion ? 'items-end' : 'items-start']"
|
||||
>
|
||||
<p
|
||||
class="text-xs text-[#b4bbc4]"
|
||||
:class="[inversion ? 'text-right' : 'text-left']"
|
||||
>
|
||||
{{ dateTime }}
|
||||
</p>
|
||||
<div
|
||||
@@ -119,11 +128,12 @@ function handleRegenerate() {
|
||||
@regenerate="handleRegenerate"
|
||||
@copy="handleCopy"
|
||||
@delete="handleDetele"
|
||||
:imageUrl="imageUrl"
|
||||
/>
|
||||
<div class="flex flex-col">
|
||||
<button
|
||||
v-if="!inversion"
|
||||
class=" flex mb-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
|
||||
class="flex mb-2 transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-300"
|
||||
@click="handleRegenerate"
|
||||
>
|
||||
<SvgIcon icon="ri:restart-line" />
|
||||
@@ -134,7 +144,9 @@ function handleRegenerate() {
|
||||
:options="options"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<button class="transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-200">
|
||||
<button
|
||||
class="transition text-neutral-300 hover:text-neutral-800 dark:hover:text-neutral-200"
|
||||
>
|
||||
<SvgIcon icon="ri:more-2-fill" />
|
||||
</button>
|
||||
</NDropdown>
|
||||
|
||||
Reference in New Issue
Block a user