|
@@ -61,372 +61,372 @@ const isChat = computed(() => appStore.isChat)
|
|
|
|
|
|
// 未知原因刷新页面,loading 状态不会重置,手动重置
|
|
|
dataSources.value.forEach((item, index) => {
|
|
|
- if (item.loading)
|
|
|
- updateChatSome(+uuid, index, { loading: false })
|
|
|
+ if (item.loading)
|
|
|
+ updateChatSome(+uuid, index, { loading: false })
|
|
|
})
|
|
|
|
|
|
function handleSubmit() {
|
|
|
- //onConversation() //把这个放到aiGpt
|
|
|
- let message = prompt.value;
|
|
|
- if (!message || message.trim() === '')
|
|
|
- return
|
|
|
- if (loading.value) return;
|
|
|
- loading.value = true
|
|
|
- homeStore.setMyData({act:'gpt.submit', actData:{ prompt:prompt.value, uuid } });
|
|
|
- prompt.value='';
|
|
|
+ //onConversation() //把这个放到aiGpt
|
|
|
+ let message = prompt.value;
|
|
|
+ if (!message || message.trim() === '')
|
|
|
+ return
|
|
|
+ if (loading.value) return;
|
|
|
+ loading.value = true
|
|
|
+ homeStore.setMyData({act:'gpt.submit', actData:{ prompt:prompt.value, uuid } });
|
|
|
+ prompt.value='';
|
|
|
}
|
|
|
|
|
|
async function onConversation() {
|
|
|
- let message = prompt.value
|
|
|
-
|
|
|
- if (loading.value)
|
|
|
- return
|
|
|
-
|
|
|
- if (!message || message.trim() === '')
|
|
|
- return
|
|
|
-
|
|
|
- controller = new AbortController()
|
|
|
-
|
|
|
- addChat(
|
|
|
- +uuid,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: message,
|
|
|
- inversion: true,
|
|
|
- error: false,
|
|
|
- conversationOptions: null,
|
|
|
- requestOptions: { prompt: message, options: null },
|
|
|
- },
|
|
|
- )
|
|
|
- scrollToBottom()
|
|
|
-
|
|
|
- loading.value = true
|
|
|
- prompt.value = ''
|
|
|
-
|
|
|
- let options: Chat.ConversationRequest = {}
|
|
|
- const lastContext = conversationList.value[conversationList.value.length - 1]?.conversationOptions
|
|
|
-
|
|
|
- if (lastContext && usingContext.value)
|
|
|
- options = { ...lastContext }
|
|
|
-
|
|
|
- addChat(
|
|
|
- +uuid,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: '思考中',
|
|
|
- loading: true,
|
|
|
- inversion: false,
|
|
|
- error: false,
|
|
|
- conversationOptions: null,
|
|
|
- requestOptions: { prompt: message, options: { ...options } },
|
|
|
- },
|
|
|
- )
|
|
|
- scrollToBottom()
|
|
|
-
|
|
|
- try {
|
|
|
- let lastText = ''
|
|
|
- const fetchChatAPIOnce = async () => {
|
|
|
- await fetchChatAPIProcess<Chat.ConversationResponse>({
|
|
|
- prompt: message,
|
|
|
- options,
|
|
|
- signal: controller.signal,
|
|
|
- onDownloadProgress: ({ event }) => {
|
|
|
- const xhr = event.target
|
|
|
- const { responseText } = xhr
|
|
|
- // Always process the final line
|
|
|
- const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
|
|
|
- let chunk = responseText
|
|
|
- if (lastIndex !== -1)
|
|
|
- chunk = responseText.substring(lastIndex)
|
|
|
- try {
|
|
|
- const data = JSON.parse(chunk)
|
|
|
- updateChat(
|
|
|
- +uuid,
|
|
|
- dataSources.value.length - 1,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: lastText + (data.text ?? ''),
|
|
|
- inversion: false,
|
|
|
- error: false,
|
|
|
- loading: true,
|
|
|
- conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
|
|
- requestOptions: { prompt: message, options: { ...options } },
|
|
|
- },
|
|
|
- )
|
|
|
-
|
|
|
- if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
|
|
|
- options.parentMessageId = data.id
|
|
|
- lastText = data.text
|
|
|
- message = ''
|
|
|
- return fetchChatAPIOnce()
|
|
|
- }
|
|
|
-
|
|
|
- scrollToBottomIfAtBottom()
|
|
|
- }
|
|
|
- catch (error) {
|
|
|
- //
|
|
|
- }
|
|
|
- },
|
|
|
- })
|
|
|
- updateChatSome(+uuid, dataSources.value.length - 1, { loading: false })
|
|
|
- }
|
|
|
-
|
|
|
- await fetchChatAPIOnce()
|
|
|
- }
|
|
|
- catch (error: any) {
|
|
|
- const errorMessage = error?.message ?? t('common.wrong')
|
|
|
-
|
|
|
- if (error.message === 'canceled') {
|
|
|
- updateChatSome(
|
|
|
- +uuid,
|
|
|
- dataSources.value.length - 1,
|
|
|
- {
|
|
|
- loading: false,
|
|
|
- },
|
|
|
- )
|
|
|
- scrollToBottomIfAtBottom()
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const currentChat = getChatByUuidAndIndex(+uuid, dataSources.value.length - 1)
|
|
|
-
|
|
|
- if (currentChat?.text && currentChat.text !== '') {
|
|
|
- updateChatSome(
|
|
|
- +uuid,
|
|
|
- dataSources.value.length - 1,
|
|
|
- {
|
|
|
- text: `${currentChat.text}\n[${errorMessage}]`,
|
|
|
- error: false,
|
|
|
- loading: false,
|
|
|
- },
|
|
|
- )
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- updateChat(
|
|
|
- +uuid,
|
|
|
- dataSources.value.length - 1,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: errorMessage,
|
|
|
- inversion: false,
|
|
|
- error: true,
|
|
|
- loading: false,
|
|
|
- conversationOptions: null,
|
|
|
- requestOptions: { prompt: message, options: { ...options } },
|
|
|
- },
|
|
|
- )
|
|
|
- scrollToBottomIfAtBottom()
|
|
|
- }
|
|
|
- finally {
|
|
|
- loading.value = false
|
|
|
- }
|
|
|
+ let message = prompt.value
|
|
|
+
|
|
|
+ if (loading.value)
|
|
|
+ return
|
|
|
+
|
|
|
+ if (!message || message.trim() === '')
|
|
|
+ return
|
|
|
+
|
|
|
+ controller = new AbortController()
|
|
|
+
|
|
|
+ addChat(
|
|
|
+ +uuid,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: message,
|
|
|
+ inversion: true,
|
|
|
+ error: false,
|
|
|
+ conversationOptions: null,
|
|
|
+ requestOptions: { prompt: message, options: null },
|
|
|
+ },
|
|
|
+ )
|
|
|
+ scrollToBottom()
|
|
|
+
|
|
|
+ loading.value = true
|
|
|
+ prompt.value = ''
|
|
|
+
|
|
|
+ let options: Chat.ConversationRequest = {}
|
|
|
+ const lastContext = conversationList.value[conversationList.value.length - 1]?.conversationOptions
|
|
|
+
|
|
|
+ if (lastContext && usingContext.value)
|
|
|
+ options = { ...lastContext }
|
|
|
+
|
|
|
+ addChat(
|
|
|
+ +uuid,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: '思考中',
|
|
|
+ loading: true,
|
|
|
+ inversion: false,
|
|
|
+ error: false,
|
|
|
+ conversationOptions: null,
|
|
|
+ requestOptions: { prompt: message, options: { ...options } },
|
|
|
+ },
|
|
|
+ )
|
|
|
+ scrollToBottom()
|
|
|
+
|
|
|
+ try {
|
|
|
+ let lastText = ''
|
|
|
+ const fetchChatAPIOnce = async () => {
|
|
|
+ await fetchChatAPIProcess<Chat.ConversationResponse>({
|
|
|
+ prompt: message,
|
|
|
+ options,
|
|
|
+ signal: controller.signal,
|
|
|
+ onDownloadProgress: ({ event }) => {
|
|
|
+ const xhr = event.target
|
|
|
+ const { responseText } = xhr
|
|
|
+ // Always process the final line
|
|
|
+ const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
|
|
|
+ let chunk = responseText
|
|
|
+ if (lastIndex !== -1)
|
|
|
+ chunk = responseText.substring(lastIndex)
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(chunk)
|
|
|
+ updateChat(
|
|
|
+ +uuid,
|
|
|
+ dataSources.value.length - 1,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: lastText + (data.text ?? ''),
|
|
|
+ inversion: false,
|
|
|
+ error: false,
|
|
|
+ loading: true,
|
|
|
+ conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
|
|
+ requestOptions: { prompt: message, options: { ...options } },
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
|
|
|
+ options.parentMessageId = data.id
|
|
|
+ lastText = data.text
|
|
|
+ message = ''
|
|
|
+ return fetchChatAPIOnce()
|
|
|
+ }
|
|
|
+
|
|
|
+ scrollToBottomIfAtBottom()
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ //
|
|
|
+ }
|
|
|
+ },
|
|
|
+ })
|
|
|
+ updateChatSome(+uuid, dataSources.value.length - 1, { loading: false })
|
|
|
+ }
|
|
|
+
|
|
|
+ await fetchChatAPIOnce()
|
|
|
+ }
|
|
|
+ catch (error: any) {
|
|
|
+ const errorMessage = error?.message ?? t('common.wrong')
|
|
|
+
|
|
|
+ if (error.message === 'canceled') {
|
|
|
+ updateChatSome(
|
|
|
+ +uuid,
|
|
|
+ dataSources.value.length - 1,
|
|
|
+ {
|
|
|
+ loading: false,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ scrollToBottomIfAtBottom()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const currentChat = getChatByUuidAndIndex(+uuid, dataSources.value.length - 1)
|
|
|
+
|
|
|
+ if (currentChat?.text && currentChat.text !== '') {
|
|
|
+ updateChatSome(
|
|
|
+ +uuid,
|
|
|
+ dataSources.value.length - 1,
|
|
|
+ {
|
|
|
+ text: `${currentChat.text}\n[${errorMessage}]`,
|
|
|
+ error: false,
|
|
|
+ loading: false,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ updateChat(
|
|
|
+ +uuid,
|
|
|
+ dataSources.value.length - 1,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: errorMessage,
|
|
|
+ inversion: false,
|
|
|
+ error: true,
|
|
|
+ loading: false,
|
|
|
+ conversationOptions: null,
|
|
|
+ requestOptions: { prompt: message, options: { ...options } },
|
|
|
+ },
|
|
|
+ )
|
|
|
+ scrollToBottomIfAtBottom()
|
|
|
+ }
|
|
|
+ finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async function onRegenerate(index: number) {
|
|
|
- if (loading.value)
|
|
|
- return
|
|
|
-
|
|
|
- controller = new AbortController()
|
|
|
-
|
|
|
- const { requestOptions } = dataSources.value[index]
|
|
|
-
|
|
|
- let message = requestOptions?.prompt ?? ''
|
|
|
-
|
|
|
- let options: Chat.ConversationRequest = {}
|
|
|
-
|
|
|
- if (requestOptions.options)
|
|
|
- options = { ...requestOptions.options }
|
|
|
-
|
|
|
- loading.value = true
|
|
|
-
|
|
|
- updateChat(
|
|
|
- +uuid,
|
|
|
- index,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: '',
|
|
|
- inversion: false,
|
|
|
- error: false,
|
|
|
- loading: true,
|
|
|
- conversationOptions: null,
|
|
|
- requestOptions: { prompt: message, options: { ...options } },
|
|
|
- },
|
|
|
- )
|
|
|
-
|
|
|
- try {
|
|
|
- let lastText = ''
|
|
|
- const fetchChatAPIOnce = async () => {
|
|
|
- await fetchChatAPIProcess<Chat.ConversationResponse>({
|
|
|
- prompt: message,
|
|
|
- options,
|
|
|
- signal: controller.signal,
|
|
|
- onDownloadProgress: ({ event }) => {
|
|
|
- const xhr = event.target
|
|
|
- const { responseText } = xhr
|
|
|
- // Always process the final line
|
|
|
- const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
|
|
|
- let chunk = responseText
|
|
|
- if (lastIndex !== -1)
|
|
|
- chunk = responseText.substring(lastIndex)
|
|
|
- try {
|
|
|
- const data = JSON.parse(chunk)
|
|
|
- updateChat(
|
|
|
- +uuid,
|
|
|
- index,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: lastText + (data.text ?? ''),
|
|
|
- inversion: false,
|
|
|
- error: false,
|
|
|
- loading: true,
|
|
|
- conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
|
|
- requestOptions: { prompt: message, options: { ...options } },
|
|
|
- },
|
|
|
- )
|
|
|
-
|
|
|
- if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
|
|
|
- options.parentMessageId = data.id
|
|
|
- lastText = data.text
|
|
|
- message = ''
|
|
|
- return fetchChatAPIOnce()
|
|
|
- }
|
|
|
- }
|
|
|
- catch (error) {
|
|
|
- //
|
|
|
- }
|
|
|
- },
|
|
|
- })
|
|
|
- updateChatSome(+uuid, index, { loading: false })
|
|
|
- }
|
|
|
- await fetchChatAPIOnce()
|
|
|
- }
|
|
|
- catch (error: any) {
|
|
|
- if (error.message === 'canceled') {
|
|
|
- updateChatSome(
|
|
|
- +uuid,
|
|
|
- index,
|
|
|
- {
|
|
|
- loading: false,
|
|
|
- },
|
|
|
- )
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const errorMessage = error?.message ?? t('common.wrong')
|
|
|
-
|
|
|
- updateChat(
|
|
|
- +uuid,
|
|
|
- index,
|
|
|
- {
|
|
|
- dateTime: new Date().toLocaleString(),
|
|
|
- text: errorMessage,
|
|
|
- inversion: false,
|
|
|
- error: true,
|
|
|
- loading: false,
|
|
|
- conversationOptions: null,
|
|
|
- requestOptions: { prompt: message, options: { ...options } },
|
|
|
- },
|
|
|
- )
|
|
|
- }
|
|
|
- finally {
|
|
|
- loading.value = false
|
|
|
- }
|
|
|
+ if (loading.value)
|
|
|
+ return
|
|
|
+
|
|
|
+ controller = new AbortController()
|
|
|
+
|
|
|
+ const { requestOptions } = dataSources.value[index]
|
|
|
+
|
|
|
+ let message = requestOptions?.prompt ?? ''
|
|
|
+
|
|
|
+ let options: Chat.ConversationRequest = {}
|
|
|
+
|
|
|
+ if (requestOptions.options)
|
|
|
+ options = { ...requestOptions.options }
|
|
|
+
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ updateChat(
|
|
|
+ +uuid,
|
|
|
+ index,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: '',
|
|
|
+ inversion: false,
|
|
|
+ error: false,
|
|
|
+ loading: true,
|
|
|
+ conversationOptions: null,
|
|
|
+ requestOptions: { prompt: message, options: { ...options } },
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ try {
|
|
|
+ let lastText = ''
|
|
|
+ const fetchChatAPIOnce = async () => {
|
|
|
+ await fetchChatAPIProcess<Chat.ConversationResponse>({
|
|
|
+ prompt: message,
|
|
|
+ options,
|
|
|
+ signal: controller.signal,
|
|
|
+ onDownloadProgress: ({ event }) => {
|
|
|
+ const xhr = event.target
|
|
|
+ const { responseText } = xhr
|
|
|
+ // Always process the final line
|
|
|
+ const lastIndex = responseText.lastIndexOf('\n', responseText.length - 2)
|
|
|
+ let chunk = responseText
|
|
|
+ if (lastIndex !== -1)
|
|
|
+ chunk = responseText.substring(lastIndex)
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(chunk)
|
|
|
+ updateChat(
|
|
|
+ +uuid,
|
|
|
+ index,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: lastText + (data.text ?? ''),
|
|
|
+ inversion: false,
|
|
|
+ error: false,
|
|
|
+ loading: true,
|
|
|
+ conversationOptions: { conversationId: data.conversationId, parentMessageId: data.id },
|
|
|
+ requestOptions: { prompt: message, options: { ...options } },
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ if (openLongReply && data.detail.choices[0].finish_reason === 'length') {
|
|
|
+ options.parentMessageId = data.id
|
|
|
+ lastText = data.text
|
|
|
+ message = ''
|
|
|
+ return fetchChatAPIOnce()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (error) {
|
|
|
+ //
|
|
|
+ }
|
|
|
+ },
|
|
|
+ })
|
|
|
+ updateChatSome(+uuid, index, { loading: false })
|
|
|
+ }
|
|
|
+ await fetchChatAPIOnce()
|
|
|
+ }
|
|
|
+ catch (error: any) {
|
|
|
+ if (error.message === 'canceled') {
|
|
|
+ updateChatSome(
|
|
|
+ +uuid,
|
|
|
+ index,
|
|
|
+ {
|
|
|
+ loading: false,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const errorMessage = error?.message ?? t('common.wrong')
|
|
|
+
|
|
|
+ updateChat(
|
|
|
+ +uuid,
|
|
|
+ index,
|
|
|
+ {
|
|
|
+ dateTime: new Date().toLocaleString(),
|
|
|
+ text: errorMessage,
|
|
|
+ inversion: false,
|
|
|
+ error: true,
|
|
|
+ loading: false,
|
|
|
+ conversationOptions: null,
|
|
|
+ requestOptions: { prompt: message, options: { ...options } },
|
|
|
+ },
|
|
|
+ )
|
|
|
+ }
|
|
|
+ finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function handleExport() {
|
|
|
- if (loading.value)
|
|
|
- return
|
|
|
-
|
|
|
- const d = dialog.warning({
|
|
|
- title: t('chat.exportImage'),
|
|
|
- content: t('chat.exportImageConfirm'),
|
|
|
- positiveText: t('common.yes'),
|
|
|
- negativeText: t('common.no'),
|
|
|
- onPositiveClick: async () => {
|
|
|
- try {
|
|
|
- d.loading = true
|
|
|
- const ele = document.getElementById('image-wrapper')
|
|
|
- const canvas = await html2canvas(ele as HTMLDivElement, {
|
|
|
- useCORS: true,
|
|
|
- })
|
|
|
- const imgUrl = canvas.toDataURL('image/png')
|
|
|
- const tempLink = document.createElement('a')
|
|
|
- tempLink.style.display = 'none'
|
|
|
- tempLink.href = imgUrl
|
|
|
- tempLink.setAttribute('download', 'chat-shot.png')
|
|
|
- if (typeof tempLink.download === 'undefined')
|
|
|
- tempLink.setAttribute('target', '_blank')
|
|
|
-
|
|
|
- document.body.appendChild(tempLink)
|
|
|
- tempLink.click()
|
|
|
- document.body.removeChild(tempLink)
|
|
|
- window.URL.revokeObjectURL(imgUrl)
|
|
|
- d.loading = false
|
|
|
- ms.success(t('chat.exportSuccess'))
|
|
|
- Promise.resolve()
|
|
|
- }
|
|
|
- catch (error: any) {
|
|
|
- ms.error(t('chat.exportFailed'))
|
|
|
- }
|
|
|
- finally {
|
|
|
- d.loading = false
|
|
|
- }
|
|
|
- },
|
|
|
- })
|
|
|
+ if (loading.value)
|
|
|
+ return
|
|
|
+
|
|
|
+ const d = dialog.warning({
|
|
|
+ title: t('chat.exportImage'),
|
|
|
+ content: t('chat.exportImageConfirm'),
|
|
|
+ positiveText: t('common.yes'),
|
|
|
+ negativeText: t('common.no'),
|
|
|
+ onPositiveClick: async () => {
|
|
|
+ try {
|
|
|
+ d.loading = true
|
|
|
+ const ele = document.getElementById('image-wrapper')
|
|
|
+ const canvas = await html2canvas(ele as HTMLDivElement, {
|
|
|
+ useCORS: true,
|
|
|
+ })
|
|
|
+ const imgUrl = canvas.toDataURL('image/png')
|
|
|
+ const tempLink = document.createElement('a')
|
|
|
+ tempLink.style.display = 'none'
|
|
|
+ tempLink.href = imgUrl
|
|
|
+ tempLink.setAttribute('download', 'chat-shot.png')
|
|
|
+ if (typeof tempLink.download === 'undefined')
|
|
|
+ tempLink.setAttribute('target', '_blank')
|
|
|
+
|
|
|
+ document.body.appendChild(tempLink)
|
|
|
+ tempLink.click()
|
|
|
+ document.body.removeChild(tempLink)
|
|
|
+ window.URL.revokeObjectURL(imgUrl)
|
|
|
+ d.loading = false
|
|
|
+ ms.success(t('chat.exportSuccess'))
|
|
|
+ Promise.resolve()
|
|
|
+ }
|
|
|
+ catch (error: any) {
|
|
|
+ ms.error(t('chat.exportFailed'))
|
|
|
+ }
|
|
|
+ finally {
|
|
|
+ d.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function handleDelete(index: number) {
|
|
|
- if (loading.value)
|
|
|
- return
|
|
|
-
|
|
|
- dialog.warning({
|
|
|
- title: t('chat.deleteMessage'),
|
|
|
- content: t('chat.deleteMessageConfirm'),
|
|
|
- positiveText: t('common.yes'),
|
|
|
- negativeText: t('common.no'),
|
|
|
- onPositiveClick: () => {
|
|
|
- chatStore.deleteChatByUuid(+uuid, index)
|
|
|
- },
|
|
|
- })
|
|
|
+ if (loading.value)
|
|
|
+ return
|
|
|
+
|
|
|
+ dialog.warning({
|
|
|
+ title: t('chat.deleteMessage'),
|
|
|
+ content: t('chat.deleteMessageConfirm'),
|
|
|
+ positiveText: t('common.yes'),
|
|
|
+ negativeText: t('common.no'),
|
|
|
+ onPositiveClick: () => {
|
|
|
+ chatStore.deleteChatByUuid(+uuid, index)
|
|
|
+ },
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function handleClear() {
|
|
|
- if (loading.value)
|
|
|
- return
|
|
|
-
|
|
|
- dialog.warning({
|
|
|
- title: t('chat.clearChat'),
|
|
|
- content: t('chat.clearChatConfirm'),
|
|
|
- positiveText: t('common.yes'),
|
|
|
- negativeText: t('common.no'),
|
|
|
- onPositiveClick: () => {
|
|
|
- chatStore.clearChatByUuid(+uuid)
|
|
|
- },
|
|
|
- })
|
|
|
+ if (loading.value)
|
|
|
+ return
|
|
|
+
|
|
|
+ dialog.warning({
|
|
|
+ title: t('chat.clearChat'),
|
|
|
+ content: t('chat.clearChatConfirm'),
|
|
|
+ positiveText: t('common.yes'),
|
|
|
+ negativeText: t('common.no'),
|
|
|
+ onPositiveClick: () => {
|
|
|
+ chatStore.clearChatByUuid(+uuid)
|
|
|
+ },
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function handleEnter(event: KeyboardEvent) {
|
|
|
- if (!isMobile.value) {
|
|
|
- if (event.key === 'Enter' && !event.shiftKey) {
|
|
|
- event.preventDefault()
|
|
|
- handleSubmit()
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- if (event.key === 'Enter' && event.ctrlKey) {
|
|
|
- event.preventDefault()
|
|
|
- handleSubmit()
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!isMobile.value) {
|
|
|
+ if (event.key === 'Enter' && !event.shiftKey) {
|
|
|
+ event.preventDefault()
|
|
|
+ handleSubmit()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (event.key === 'Enter' && event.ctrlKey) {
|
|
|
+ event.preventDefault()
|
|
|
+ handleSubmit()
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function handleStop() {
|
|
|
- if (loading.value) {
|
|
|
- homeStore.setMyData({act:'abort'});
|
|
|
- controller.abort()
|
|
|
- loading.value = false
|
|
|
- }
|
|
|
+ if (loading.value) {
|
|
|
+ homeStore.setMyData({act:'abort'});
|
|
|
+ controller.abort()
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
@@ -436,122 +436,122 @@ function handleStop() {
|
|
|
// 搜索选项计算,这里使用value作为索引项,所以当出现重复value时渲染异常(多项同时出现选中效果)
|
|
|
// 理想状态下其实应该是key作为索引项,但官方的renderOption会出现问题,所以就需要value反renderLabel实现
|
|
|
const searchOptions = computed(() => {
|
|
|
- if (prompt.value.startsWith('/')) {
|
|
|
- const abc= promptTemplate.value.filter((item: { key: string }) => item.key.toLowerCase().includes(prompt.value.substring(1).toLowerCase())).map((obj: { value: any }) => {
|
|
|
- return {
|
|
|
- label: obj.value,
|
|
|
- value: obj.value,
|
|
|
- }
|
|
|
- })
|
|
|
- mlog('搜索选项', abc);
|
|
|
- return abc;
|
|
|
- }else if(prompt.value=='@'){
|
|
|
- const abc= gptsUlistStore.myData.slice(0,10).map( (v:gptsType) => {
|
|
|
- return {
|
|
|
- label:v.info,
|
|
|
- gpts:v,
|
|
|
- value:v.gid
|
|
|
- }
|
|
|
- })
|
|
|
- return abc ;
|
|
|
- }else {
|
|
|
- return []
|
|
|
- }
|
|
|
+ if (prompt.value.startsWith('/')) {
|
|
|
+ const abc= promptTemplate.value.filter((item: { key: string }) => item.key.toLowerCase().includes(prompt.value.substring(1).toLowerCase())).map((obj: { value: any }) => {
|
|
|
+ return {
|
|
|
+ label: obj.value,
|
|
|
+ value: obj.value,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ mlog('搜索选项', abc);
|
|
|
+ return abc;
|
|
|
+ }else if(prompt.value=='@'){
|
|
|
+ const abc= gptsUlistStore.myData.slice(0,10).map( (v:gptsType) => {
|
|
|
+ return {
|
|
|
+ label:v.info,
|
|
|
+ gpts:v,
|
|
|
+ value:v.gid
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return abc ;
|
|
|
+ }else {
|
|
|
+ return []
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
const goUseGpts= async ( item: gptsType)=>{
|
|
|
- const saveObj= {model: `${ item.gid }` ,gpts:item}
|
|
|
- gptConfigStore.setMyData(saveObj);
|
|
|
- if(chatStore.active){ //保存到对话框
|
|
|
- const chatSet = new chatSetting( chatStore.active );
|
|
|
- if( chatSet.findIndex()>-1 ) chatSet.save( saveObj )
|
|
|
- }
|
|
|
- ms.success(t('mjchat.success2'));
|
|
|
- // const gptUrl= `https://gpts.ddaiai.com/open/gptsapi/use`;
|
|
|
- // myFetch(gptUrl,item );
|
|
|
+ const saveObj= {model: `${ item.gid }` ,gpts:item}
|
|
|
+ gptConfigStore.setMyData(saveObj);
|
|
|
+ if(chatStore.active){ //保存到对话框
|
|
|
+ const chatSet = new chatSetting( chatStore.active );
|
|
|
+ if( chatSet.findIndex()>-1 ) chatSet.save( saveObj )
|
|
|
+ }
|
|
|
+ ms.success(t('mjchat.success2'));
|
|
|
+ // const gptUrl= `https://gpts.ddaiai.com/open/gptsapi/use`;
|
|
|
+ // myFetch(gptUrl,item );
|
|
|
|
|
|
- mlog('go local ', homeStore.myData.local );
|
|
|
- if(homeStore.myData.local!=='Chat') router.replace({name:'Chat',params:{uuid:chatStore.active}});
|
|
|
+ mlog('go local ', homeStore.myData.local );
|
|
|
+ if(homeStore.myData.local!=='Chat') router.replace({name:'Chat',params:{uuid:chatStore.active}});
|
|
|
|
|
|
- gptsUlistStore.setMyData( item );
|
|
|
+ gptsUlistStore.setMyData( item );
|
|
|
|
|
|
}
|
|
|
|
|
|
// value反渲染key
|
|
|
const renderOption = (option: { label: string,gpts?:gptsType }) => {
|
|
|
- if( prompt.value=='@'){
|
|
|
- //return [ h( NAvatar,{src:'https://cos.aitutu.cc/gpts/gpt4all.jpg',size:"small",round:true}),option.label ]
|
|
|
- return [h("div",{class:'flex justify-start items-center'
|
|
|
- , onclick:()=>{
|
|
|
- if(option.gpts) goUseGpts(option.gpts) ;
|
|
|
- prompt.value='';
|
|
|
- setTimeout(() => prompt.value='', 80);
|
|
|
- }}
|
|
|
- ,[h(NAvatar,{src:option.gpts?.logo, "fallback-src" : 'https://cos.aitutu.cc/gpts/3.5net.png',size:"small",round:true, class:"w-8 h-8"})
|
|
|
- , h('span', { class: 'pl-1' }, option.gpts?.name )
|
|
|
- , h('span', { class: 'line-clamp-1 flex-1 pl-1 opacity-50' }, option.label )
|
|
|
- ])]
|
|
|
- }
|
|
|
- for (const i of promptTemplate.value) {
|
|
|
- if (i.value === option.label)
|
|
|
- return [i.key]
|
|
|
- }
|
|
|
- return []
|
|
|
+ if( prompt.value=='@'){
|
|
|
+ //return [ h( NAvatar,{src:'https://cos.aitutu.cc/gpts/gpt4all.jpg',size:"small",round:true}),option.label ]
|
|
|
+ return [h("div",{class:'flex justify-start items-center'
|
|
|
+ , onclick:()=>{
|
|
|
+ if(option.gpts) goUseGpts(option.gpts) ;
|
|
|
+ prompt.value='';
|
|
|
+ setTimeout(() => prompt.value='', 80);
|
|
|
+ }}
|
|
|
+ ,[h(NAvatar,{src:option.gpts?.logo, "fallback-src" : 'https://cos.aitutu.cc/gpts/3.5net.png',size:"small",round:true, class:"w-8 h-8"})
|
|
|
+ , h('span', { class: 'pl-1' }, option.gpts?.name )
|
|
|
+ , h('span', { class: 'line-clamp-1 flex-1 pl-1 opacity-50' }, option.label )
|
|
|
+ ])]
|
|
|
+ }
|
|
|
+ for (const i of promptTemplate.value) {
|
|
|
+ if (i.value === option.label)
|
|
|
+ return [i.key]
|
|
|
+ }
|
|
|
+ return []
|
|
|
}
|
|
|
|
|
|
const placeholder = computed(() => {
|
|
|
- if (isMobile.value)
|
|
|
- return t('chat.placeholderMobile')
|
|
|
- return t('chat.placeholder')
|
|
|
+ if (isMobile.value)
|
|
|
+ return t('chat.placeholderMobile')
|
|
|
+ return t('chat.placeholder')
|
|
|
})
|
|
|
|
|
|
const buttonDisabled = computed(() => {
|
|
|
- return loading.value || !prompt.value || prompt.value.trim() === ''
|
|
|
+ return loading.value || !prompt.value || prompt.value.trim() === ''
|
|
|
})
|
|
|
|
|
|
const footerClass = computed(() => {
|
|
|
- let classes = ['p-4']
|
|
|
- if (isMobile.value)
|
|
|
- classes = ['sticky', 'left-0', 'bottom-0', 'right-0', 'p-2', 'pr-3'] //, 'overflow-hidden'
|
|
|
- return classes
|
|
|
+ let classes = ['p-4']
|
|
|
+ if (isMobile.value)
|
|
|
+ classes = ['sticky', 'left-0', 'bottom-0', 'right-0', 'p-2', 'pr-3'] //, 'overflow-hidden'
|
|
|
+ return classes
|
|
|
})
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
- scrollToBottom()
|
|
|
- if (inputRef.value && !isMobile.value)
|
|
|
- inputRef.value?.focus()
|
|
|
- // 查询公告信息
|
|
|
- selectNotice()
|
|
|
- // 查询通知信息
|
|
|
- selectInform()
|
|
|
+ scrollToBottom()
|
|
|
+ if (inputRef.value && !isMobile.value)
|
|
|
+ inputRef.value?.focus()
|
|
|
+ // 查询公告信息
|
|
|
+ selectNotice()
|
|
|
+ // 查询通知信息
|
|
|
+ selectInform()
|
|
|
})
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
- if (loading.value) controller.abort()
|
|
|
- homeStore.setMyData({isLoader:false});
|
|
|
+ if (loading.value) controller.abort()
|
|
|
+ homeStore.setMyData({isLoader:false});
|
|
|
})
|
|
|
|
|
|
const local= computed(()=>homeStore.myData.local );
|
|
|
watch(()=>homeStore.myData.act,(n)=>{
|
|
|
- if(n=='draw') scrollToBottom();
|
|
|
- if(n=='scrollToBottom') scrollToBottom();
|
|
|
- if(n=='scrollToBottomIfAtBottom') scrollToBottomIfAtBottom();
|
|
|
- if(n=='gpt.submit' || n=='gpt.resubmit'){ loading.value = true;}
|
|
|
- if(n=='stopLoading'){ loading.value = false;}
|
|
|
+ if(n=='draw') scrollToBottom();
|
|
|
+ if(n=='scrollToBottom') scrollToBottom();
|
|
|
+ if(n=='scrollToBottomIfAtBottom') scrollToBottomIfAtBottom();
|
|
|
+ if(n=='gpt.submit' || n=='gpt.resubmit'){ loading.value = true;}
|
|
|
+ if(n=='stopLoading'){ loading.value = false;}
|
|
|
});
|
|
|
const st =ref({inputme:true});
|
|
|
|
|
|
watch( ()=>loading.value ,(n)=> homeStore.setMyData({isLoader:n }))
|
|
|
|
|
|
const ychat = computed( ()=>{
|
|
|
- let text= prompt.value
|
|
|
- if (loading.value) text= "";
|
|
|
- else {
|
|
|
- scrollToBottomIfAtBottom();
|
|
|
- }
|
|
|
- return { text, dateTime: t('chat.preview')} as Chat.Chat;
|
|
|
+ let text= prompt.value
|
|
|
+ if (loading.value) text= "";
|
|
|
+ else {
|
|
|
+ scrollToBottomIfAtBottom();
|
|
|
+ }
|
|
|
+ return { text, dateTime: t('chat.preview')} as Chat.Chat;
|
|
|
})
|
|
|
|
|
|
const showModal = ref(false);
|
|
@@ -559,259 +559,254 @@ const modalContent = ref('<h2>暂无内容</h2>');
|
|
|
const informContent = ref([]);
|
|
|
const noticeId = ref('');
|
|
|
async function selectNotice() {
|
|
|
- const [err, result] = await to(getNotice());
|
|
|
- console.log("result?.data",result?.data)
|
|
|
- if (result?.data) {
|
|
|
- showModal.value = true
|
|
|
- noticeId.value = result.data.noticeId
|
|
|
- modalContent.value = result.data.noticeContent
|
|
|
- }
|
|
|
+ const [err, result] = await to(getNotice());
|
|
|
+ console.log("result?.data",result?.data)
|
|
|
+ if (result?.data) {
|
|
|
+ showModal.value = true
|
|
|
+ noticeId.value = result.data.noticeId
|
|
|
+ modalContent.value = result.data.noticeContent
|
|
|
+ }
|
|
|
}
|
|
|
async function selectInform() {
|
|
|
- const [err, result] = await to(getInform());
|
|
|
- if (result?.rows) {
|
|
|
- informContent.value = result.rows.length ? result.rows : []
|
|
|
- }
|
|
|
+ const [err, result] = await to(getInform());
|
|
|
+ if (result?.rows) {
|
|
|
+ informContent.value = result.rows.length ? result.rows : []
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async function handleClose(){
|
|
|
- await to(readNotice(noticeId.value));
|
|
|
+ await to(readNotice(noticeId.value));
|
|
|
}
|
|
|
const gptsList = ref<gptsType[]>([]);
|
|
|
const gptsFilterList = ref<gptsType[]>([]);
|
|
|
const getRandowNum = (Min:number, Max: number):number =>{
|
|
|
- const Range = Max - Min + 1
|
|
|
- const Rand = Math.random()
|
|
|
- return Min + Math.floor(Rand * Range)
|
|
|
+ const Range = Max - Min + 1
|
|
|
+ const Rand = Math.random()
|
|
|
+ return Min + Math.floor(Rand * Range)
|
|
|
}
|
|
|
const load= async ()=>{
|
|
|
|
|
|
- // const gptUrl= homeStore.myData.session.gptUrl? homeStore.myData.session.gptUrl :'';
|
|
|
- // mlog('load',gptUrl );
|
|
|
- // let d;
|
|
|
- // if( homeStore.myData.session.gptUrl ){
|
|
|
- // d = await my2Fetch( homeStore.myData.session.gptUrl );
|
|
|
- // }else {
|
|
|
-
|
|
|
- // d = await myFetch('https://gpts.ddaiai.com/open/gpts');
|
|
|
- // }
|
|
|
-
|
|
|
- const params = { pageNum: 1, pageSize: 20 };
|
|
|
- const [err, result] = await to(getGpts(params));
|
|
|
- if(err){
|
|
|
- console.log("err===",err)
|
|
|
- }else{
|
|
|
- gptsList.value = result.rows as unknown as gptsType[];
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // gptsList.value = d.gpts as gptsType[];
|
|
|
- if(gptsList.value.length && gptsList.value.length > 3) {
|
|
|
- gptsFilterList.value = gptsList.value.slice(0, 4)
|
|
|
- }
|
|
|
+ // const gptUrl= homeStore.myData.session.gptUrl? homeStore.myData.session.gptUrl :'';
|
|
|
+ // mlog('load',gptUrl );
|
|
|
+ // let d;
|
|
|
+ // if( homeStore.myData.session.gptUrl ){
|
|
|
+ // d = await my2Fetch( homeStore.myData.session.gptUrl );
|
|
|
+ // }else {
|
|
|
+
|
|
|
+ // d = await myFetch('https://gpts.ddaiai.com/open/gpts');
|
|
|
+ // }
|
|
|
+
|
|
|
+ const params = { pageNum: 1, pageSize: 20 };
|
|
|
+ const [err, result] = await to(getGpts(params));
|
|
|
+ if(err){
|
|
|
+ console.log("err===",err)
|
|
|
+ }else{
|
|
|
+ gptsList.value = result.rows as unknown as gptsType[];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // gptsList.value = d.gpts as gptsType[];
|
|
|
+ if(gptsList.value.length && gptsList.value.length > 3) {
|
|
|
+ gptsFilterList.value = gptsList.value.slice(0, 4)
|
|
|
+ }
|
|
|
}
|
|
|
const refresh = () => {
|
|
|
- gptsFilterList.value = []
|
|
|
- let num = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
- let num1 = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
- let num2 = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
- let num3 = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
- let arr = [num, num1, num2, num3]
|
|
|
- if(Array.from(new Set(arr)).length != 4) {
|
|
|
- refresh()
|
|
|
- return
|
|
|
- }
|
|
|
- gptsFilterList.value = [num, num1, num2, num3]
|
|
|
+ gptsFilterList.value = []
|
|
|
+ let num = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
+ let num1 = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
+ let num2 = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
+ let num3 = gptsList.value[getRandowNum(0, gptsList.value.length - 1)]
|
|
|
+ let arr = [num, num1, num2, num3]
|
|
|
+ if(Array.from(new Set(arr)).length != 4) {
|
|
|
+ refresh()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ gptsFilterList.value = [num, num1, num2, num3]
|
|
|
}
|
|
|
load()
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <NModal
|
|
|
- v-model:show="showModal"
|
|
|
- closable @on-after-leave=""
|
|
|
- :mask-closable="false"
|
|
|
- preset="dialog"
|
|
|
- title="公告详情"
|
|
|
- positive-text="我已知晓"
|
|
|
- @positive-click="handleClose"
|
|
|
- >
|
|
|
- <div v-html="modalContent"></div>
|
|
|
- </NModal>
|
|
|
-
|
|
|
- <div class="flex flex-col w-full h-full chat-content" :class="[isMobile? '' : 'chat-content-noMobile']">
|
|
|
- <!-- v-if="isMobile" -->
|
|
|
- <!-- <HeaderComponent
|
|
|
- :haveData="!!dataSources.length"
|
|
|
- :using-context="usingContext"
|
|
|
- @export="handleExport"
|
|
|
- @handle-clear="handleClear"
|
|
|
- /> -->
|
|
|
-
|
|
|
- <main class="flex-1 overflow-hidden">
|
|
|
-
|
|
|
- <template v-if="gptConfigStore.myData.kid">
|
|
|
- <div class="flex mt-4 text-neutral-300 chat-header">
|
|
|
- <SvgIcon icon="material-symbols:book" class="mr-1 text-2xl" ></SvgIcon>
|
|
|
- <span>{{ gptConfigStore.myData.kName }}</span>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <div id="scrollRef" ref="scrollRef" class="h-full overflow-hidden overflow-y-auto">
|
|
|
-
|
|
|
- <div
|
|
|
- id="image-wrapper"
|
|
|
- class="w-full max-w-screen-xl m-auto dark:bg-[#101014]"
|
|
|
- :class="[isMobile ? 'p-2' : 'p-4']"
|
|
|
- >
|
|
|
- <template v-if="!dataSources.length">
|
|
|
- <div v-if="homeStore.myData.session.notify" v-html="homeStore.myData.session.notify" class="text-neutral-300 mt-4">
|
|
|
-
|
|
|
- </div>
|
|
|
- <div class="gpts-box" v-else>
|
|
|
- <h1>{{ href }}</h1>
|
|
|
- <div class="annou" v-if="informContent.length" :style="{'margin-bottom': isMobile ? '15px' : '30px'}">
|
|
|
- <div class="ai-icon">
|
|
|
- <IconSvg icon="chatGPT" :width="isMobile ? '32px' : '64px'" :height="isMobile ? '32px' : '64px'"></IconSvg>
|
|
|
- </div>
|
|
|
- <div class="text" :style="{padding: isMobile? '22px 10px' : '22px 68px'}">
|
|
|
- <p class="title">{{ t('chat.annouce') }}</p>
|
|
|
- <!-- <p v-for="(item,index) in t('chat.annouceContent').split(';')" :key="index">{{ item }}</p> -->
|
|
|
- <div v-for="(item, index) in informContent.slice(0, 1)" :key="index" >
|
|
|
- <!-- <p style="margin-top: 10px; font-size: 18px">{{ item.noticeTitle }}</p> -->
|
|
|
- <div v-html="item.noticeContent"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="help" v-if="gptsFilterList && gptsFilterList.length">
|
|
|
- <div class="ai-icon">
|
|
|
- <IconSvg icon="chatGPT" :width="isMobile ? '32px' : '64px'" :height="isMobile ? '32px' : '64px'"></IconSvg>
|
|
|
- </div>
|
|
|
- <div class="text" :style="{padding: isMobile? '22px 10px' : '22px 68px', 'font-size': isMobile? '14px' : '16px', 'line-height': isMobile? '20px' : '28px'}">
|
|
|
- <p class="title">
|
|
|
- {{ t('chat.helpTitle') }}
|
|
|
- </p>
|
|
|
- <p v-for="(item,index) in t('chat.helpcontent').split(';')" :key="index">{{ item }}</p>
|
|
|
- <div class="gpts-list">
|
|
|
- <div class="refresh" @click="refresh">
|
|
|
- <IconSvg icon="refresh"></IconSvg> {{ t('chat.refresh') }}
|
|
|
- </div>
|
|
|
- <div v-for="v in gptsFilterList" :key="v.name" class="gpts-item" :style="{width: isMobile ? '100%' : 'calc(50% - 20px)', marginRight: '20px', padding: isMobile ? '5px 8px' : '14px 10px', 'margin-bottom': isMobile ? '8px' : '20px'}">
|
|
|
- <NImage :src="v.logo" :preview-disabled="true" lazy
|
|
|
- class="group-hover:scale-[130%] duration-300 shrink-0 overflow-hidden bg-base object-cover rounded-full bc-avatar w-[80px] h-[80px]" :style="{width: isMobile ? '23px' : '46px', height: isMobile ? '23px' : '46px'}">
|
|
|
- <template #placeholder>
|
|
|
- <div class="w-full h-full justify-center items-center flex" >
|
|
|
- <SvgIcon icon="line-md:downloading-loop" class="text-[60px] text-green-300" ></SvgIcon>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </NImage>
|
|
|
- <div :style="{width: `calc(100% - ${isMobile ? '43px' : '66px'})`, float: 'left', marginLeft: '10px'}">
|
|
|
- <p class="info" :title="v.info"> {{ v.info }}</p>
|
|
|
- <p @click="goUseGpts(v)" class="name"> {{ t('chat.used') }} {{ v.name }}</p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- <div class="flex items-center justify-center mt-4 text-center text-neutral-300" v-else>
|
|
|
- <SvgIcon icon="ri:bubble-chart-fill" class="mr-2 text-3xl" />
|
|
|
- <span>Aha~</span>
|
|
|
- </div> -->
|
|
|
- </template>
|
|
|
-
|
|
|
- <template v-else>
|
|
|
- <div>
|
|
|
- <Message
|
|
|
- v-for="(item, index) of dataSources"
|
|
|
- :key="index"
|
|
|
- :date-time="item.dateTime"
|
|
|
- :text="item.text"
|
|
|
- :inversion="item.inversion"
|
|
|
- :error="item.error"
|
|
|
- :loading="item.loading"
|
|
|
- @regenerate="onRegenerate(index)"
|
|
|
- @delete="handleDelete(index)"
|
|
|
- :chat="item"
|
|
|
- :index="index"
|
|
|
- />
|
|
|
- <Message v-if="ychat.text && !homeStore.myData.session.isCloseMdPreview"
|
|
|
- :key="dataSources.length" :inversion="true"
|
|
|
- :date-time="$t('mj.typing')"
|
|
|
- :chat="ychat"
|
|
|
- :text="ychat.text"
|
|
|
- :index="dataSources.length"
|
|
|
- />
|
|
|
- <div class="sticky bottom-0 left-0 flex justify-center">
|
|
|
- <NButton v-if="loading" type="warning" @click="handleStop">
|
|
|
- <template #icon>
|
|
|
- <SvgIcon icon="ri:stop-circle-line" />
|
|
|
- </template>
|
|
|
- {{ t('common.stopResponding') }}
|
|
|
- </NButton>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </main>
|
|
|
-
|
|
|
- <footer :class="footerClass" class="footer-content" v-if="local!=='draw'">
|
|
|
- <div class="w-full max-w-screen-xl m-auto">
|
|
|
- <aiGptInput @handle-clear="handleClear" @export="handleExport" v-if="['gpt-4o-mini','gpt-3.5-turbo-16k'].indexOf(gptConfigStore.myData.model)>-1 || st.inputme "
|
|
|
- v-model:modelValue="prompt" :disabled="buttonDisabled"
|
|
|
- :searchOptions="searchOptions" :renderOption="renderOption"
|
|
|
- />
|
|
|
- <div class="flex items-center justify-between space-x-2" v-else>
|
|
|
- <!--
|
|
|
- <HoverButton v-if="!isMobile" @click="handleClear">
|
|
|
- <span class="text-xl text-[#4f555e] dark:text-white">
|
|
|
- <SvgIcon icon="ri:delete-bin-line" />
|
|
|
- </span>
|
|
|
- </HoverButton>
|
|
|
- <HoverButton v-if="!isMobile" @click="handleExport">
|
|
|
- <span class="text-xl text-[#4f555e] dark:text-white">
|
|
|
- <SvgIcon icon="ri:download-2-line" />
|
|
|
- </span>
|
|
|
- </HoverButton>
|
|
|
- <HoverButton @click="toggleUsingContext">
|
|
|
- <span class="text-xl" :class="{ 'text-[#4b9e5f]': usingContext, 'text-[#a8071a]': !usingContext }">
|
|
|
- <SvgIcon icon="ri:chat-history-line" />
|
|
|
- </span>
|
|
|
- </HoverButton>
|
|
|
- -->
|
|
|
-
|
|
|
- <NAutoComplete v-model:value="prompt" :options="searchOptions" :render-label="renderOption">
|
|
|
- <template #default="{ handleInput, handleBlur, handleFocus }">
|
|
|
- <NInput
|
|
|
- ref="inputRef"
|
|
|
- v-model:value="prompt"
|
|
|
- type="textarea"
|
|
|
- :placeholder="placeholder"
|
|
|
- :autosize="{ minRows: 1, maxRows: isMobile ? 4 : 8 }"
|
|
|
- @input="handleInput"
|
|
|
- @focus="handleFocus"
|
|
|
- @blur="handleBlur"
|
|
|
- @keypress="handleEnter"
|
|
|
- />
|
|
|
- </template>
|
|
|
- </NAutoComplete>
|
|
|
- <NButton type="primary" :disabled="buttonDisabled" @click="handleSubmit">
|
|
|
- <template #icon>
|
|
|
+ <NModal
|
|
|
+ v-model:show="showModal"
|
|
|
+ closable @on-after-leave=""
|
|
|
+ :mask-closable="false"
|
|
|
+ preset="dialog"
|
|
|
+ title="公告详情"
|
|
|
+ positive-text="我已知晓"
|
|
|
+ @positive-click="handleClose"
|
|
|
+ >
|
|
|
+ <div v-html="modalContent"></div>
|
|
|
+ </NModal>
|
|
|
+
|
|
|
+ <div class="flex flex-col w-full h-full chat-content" :class="[isMobile? '' : 'chat-content-noMobile']">
|
|
|
+ <!-- v-if="isMobile" -->
|
|
|
+ <!-- <HeaderComponent
|
|
|
+ :haveData="!!dataSources.length"
|
|
|
+ :using-context="usingContext"
|
|
|
+ @export="handleExport"
|
|
|
+ @handle-clear="handleClear"
|
|
|
+ /> -->
|
|
|
+
|
|
|
+ <main class="flex-1 overflow-hidden">
|
|
|
+
|
|
|
+ <div id="scrollRef" ref="scrollRef" class="h-full overflow-hidden overflow-y-auto">
|
|
|
+
|
|
|
+ <div
|
|
|
+ id="image-wrapper"
|
|
|
+ class="w-full max-w-screen-xl m-auto dark:bg-[#101014]"
|
|
|
+ :class="[isMobile ? 'p-2' : 'p-4']"
|
|
|
+ >
|
|
|
+ <template v-if="!dataSources.length">
|
|
|
+ <div v-if="homeStore.myData.session.notify" v-html="homeStore.myData.session.notify" class="text-neutral-300 mt-4">
|
|
|
+
|
|
|
+ </div>
|
|
|
+ <div class="gpts-box" v-else>
|
|
|
+ <br>
|
|
|
+ <br>
|
|
|
+<!-- <h1>{{ href }}</h1>-->
|
|
|
+ <div class="annou" v-if="informContent.length" :style="{'margin-bottom': isMobile ? '15px' : '30px'}">
|
|
|
+ <div class="ai-icon">
|
|
|
+ <IconSvg icon="chatGPT" :width="isMobile ? '32px' : '64px'" :height="isMobile ? '32px' : '64px'"></IconSvg>
|
|
|
+ </div>
|
|
|
+ <div class="text" :style="{padding: isMobile? '22px 10px' : '22px 68px'}">
|
|
|
+ <p class="title">{{ t('chat.annouce') }}</p>
|
|
|
+ <!-- <p v-for="(item,index) in t('chat.annouceContent').split(';')" :key="index">{{ item }}</p> -->
|
|
|
+ <div v-for="(item, index) in informContent.slice(0, 1)" :key="index" >
|
|
|
+ <!-- <p style="margin-top: 10px; font-size: 18px">{{ item.noticeTitle }}</p> -->
|
|
|
+ <div v-html="item.noticeContent"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="help" v-if="gptsFilterList && gptsFilterList.length">
|
|
|
+ <div class="ai-icon">
|
|
|
+ <IconSvg icon="chatGPT" :width="isMobile ? '32px' : '64px'" :height="isMobile ? '32px' : '64px'"></IconSvg>
|
|
|
+ </div>
|
|
|
+ <div class="text" :style="{padding: isMobile? '22px 10px' : '22px 68px', 'font-size': isMobile? '14px' : '16px', 'line-height': isMobile? '20px' : '28px'}">
|
|
|
+ <p class="title">
|
|
|
+ {{ t('chat.helpTitle') }}
|
|
|
+ </p>
|
|
|
+ <p v-for="(item,index) in t('chat.helpcontent').split(';')" :key="index">{{ item }}</p>
|
|
|
+ <div class="gpts-list">
|
|
|
+ <div class="refresh" @click="refresh">
|
|
|
+ <IconSvg icon="refresh"></IconSvg> {{ t('chat.refresh') }}
|
|
|
+ </div>
|
|
|
+ <div v-for="v in gptsFilterList" :key="v.name" class="gpts-item" :style="{width: isMobile ? '100%' : 'calc(50% - 20px)', marginRight: '20px', padding: isMobile ? '5px 8px' : '14px 10px', 'margin-bottom': isMobile ? '8px' : '20px'}">
|
|
|
+ <NImage :src="v.logo" :preview-disabled="true" lazy
|
|
|
+ class="group-hover:scale-[130%] duration-300 shrink-0 overflow-hidden bg-base object-cover rounded-full bc-avatar w-[80px] h-[80px]" :style="{width: isMobile ? '23px' : '46px', height: isMobile ? '23px' : '46px'}">
|
|
|
+ <template #placeholder>
|
|
|
+ <div class="w-full h-full justify-center items-center flex" >
|
|
|
+ <SvgIcon icon="line-md:downloading-loop" class="text-[60px] text-green-300" ></SvgIcon>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </NImage>
|
|
|
+ <div :style="{width: `calc(100% - ${isMobile ? '43px' : '66px'})`, float: 'left', marginLeft: '10px'}">
|
|
|
+ <p class="info" :title="v.info"> {{ v.info }}</p>
|
|
|
+ <p @click="goUseGpts(v)" class="name"> {{ t('chat.used') }} {{ v.name }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- <div class="flex items-center justify-center mt-4 text-center text-neutral-300" v-else>
|
|
|
+ <SvgIcon icon="ri:bubble-chart-fill" class="mr-2 text-3xl" />
|
|
|
+ <span>Aha~</span>
|
|
|
+ </div> -->
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-else>
|
|
|
+ <div>
|
|
|
+ <Message
|
|
|
+ v-for="(item, index) of dataSources"
|
|
|
+ :key="index"
|
|
|
+ :date-time="item.dateTime"
|
|
|
+ :text="item.text"
|
|
|
+ :inversion="item.inversion"
|
|
|
+ :error="item.error"
|
|
|
+ :loading="item.loading"
|
|
|
+ @regenerate="onRegenerate(index)"
|
|
|
+ @delete="handleDelete(index)"
|
|
|
+ :chat="item"
|
|
|
+ :index="index"
|
|
|
+ />
|
|
|
+ <Message v-if="ychat.text && !homeStore.myData.session.isCloseMdPreview"
|
|
|
+ :key="dataSources.length" :inversion="true"
|
|
|
+ :date-time="$t('mj.typing')"
|
|
|
+ :chat="ychat"
|
|
|
+ :text="ychat.text"
|
|
|
+ :index="dataSources.length"
|
|
|
+ />
|
|
|
+ <div class="sticky bottom-0 left-0 flex justify-center">
|
|
|
+ <NButton v-if="loading" type="warning" @click="handleStop">
|
|
|
+ <template #icon>
|
|
|
+ <SvgIcon icon="ri:stop-circle-line" />
|
|
|
+ </template>
|
|
|
+ {{ t('common.stopResponding') }}
|
|
|
+ </NButton>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </main>
|
|
|
+
|
|
|
+ <footer :class="footerClass" class="footer-content" v-if="local!=='draw'">
|
|
|
+ <div class="w-full max-w-screen-xl m-auto">
|
|
|
+ <aiGptInput @handle-clear="handleClear" @export="handleExport" v-if="['gpt-4o-mini','gpt-3.5-turbo-16k'].indexOf(gptConfigStore.myData.model)>-1 || st.inputme "
|
|
|
+ v-model:modelValue="prompt" :disabled="buttonDisabled"
|
|
|
+ :searchOptions="searchOptions" :renderOption="renderOption"
|
|
|
+ />
|
|
|
+ <div class="flex items-center justify-between space-x-2" v-else>
|
|
|
+ <!--
|
|
|
+ <HoverButton v-if="!isMobile" @click="handleClear">
|
|
|
+ <span class="text-xl text-[#4f555e] dark:text-white">
|
|
|
+ <SvgIcon icon="ri:delete-bin-line" />
|
|
|
+ </span>
|
|
|
+ </HoverButton>
|
|
|
+ <HoverButton v-if="!isMobile" @click="handleExport">
|
|
|
+ <span class="text-xl text-[#4f555e] dark:text-white">
|
|
|
+ <SvgIcon icon="ri:download-2-line" />
|
|
|
+ </span>
|
|
|
+ </HoverButton>
|
|
|
+ <HoverButton @click="toggleUsingContext">
|
|
|
+ <span class="text-xl" :class="{ 'text-[#4b9e5f]': usingContext, 'text-[#a8071a]': !usingContext }">
|
|
|
+ <SvgIcon icon="ri:chat-history-line" />
|
|
|
+ </span>
|
|
|
+ </HoverButton>
|
|
|
+ -->
|
|
|
+
|
|
|
+ <NAutoComplete v-model:value="prompt" :options="searchOptions" :render-label="renderOption">
|
|
|
+ <template #default="{ handleInput, handleBlur, handleFocus }">
|
|
|
+ <NInput
|
|
|
+ ref="inputRef"
|
|
|
+ v-model:value="prompt"
|
|
|
+ type="textarea"
|
|
|
+ :placeholder="placeholder"
|
|
|
+ :autosize="{ minRows: 1, maxRows: isMobile ? 4 : 8 }"
|
|
|
+ @input="handleInput"
|
|
|
+ @focus="handleFocus"
|
|
|
+ @blur="handleBlur"
|
|
|
+ @keypress="handleEnter"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </NAutoComplete>
|
|
|
+ <NButton type="primary" :disabled="buttonDisabled" @click="handleSubmit">
|
|
|
+ <template #icon>
|
|
|
<span class="dark:text-black">
|
|
|
<SvgIcon icon="ri:send-plane-fill" />
|
|
|
</span>
|
|
|
- </template>
|
|
|
- </NButton>
|
|
|
+ </template>
|
|
|
+ </NButton>
|
|
|
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </footer>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </footer>
|
|
|
+ </div>
|
|
|
|
|
|
- <drawListVue />
|
|
|
- <aiGPT @finished="loading = false" />
|
|
|
- <AiSiderInput v-if="isMobile" :button-disabled="false" />
|
|
|
+ <drawListVue />
|
|
|
+ <aiGPT @finished="loading = false" />
|
|
|
+ <AiSiderInput v-if="isMobile" :button-disabled="false" />
|
|
|
|
|
|
</template>
|