Commit 320e3a25 authored by shirlyn.guo's avatar shirlyn.guo 👌🏻

Merge branch 'master' of https://gitlab.gsstcloud.com/poc/poc-fe into shirlyn

parents 5262f496 6f8d5ac9
...@@ -59,7 +59,7 @@ export default [ ...@@ -59,7 +59,7 @@ export default [
}, },
{ {
path: '/personalSpace/knowledge/document/detail/:kdId', path: '/personalSpace/knowledge/document/detail/:id/:kdId',
name: 'KnowledgeDocumentDetail', name: 'KnowledgeDocumentDetail',
meta: { meta: {
rank: 1001, rank: 1001,
......
import CMessage from './src/message'
import './src/message.scss'
export default CMessage
import { createApp } from 'vue'
import Message from './message.vue'
import { CMessageOptions } from './message'
const createInstance = (cfg: CMessageOptions) => {
const config = cfg || {}
let targetElementId = ''
let targetHTMLElement: HTMLElement | null = null
if (config.to && config.to[0] === '#') {
targetElementId = config.to.slice(1)
targetHTMLElement = document.getElementById(targetElementId)
}
// 创建包裹容器
const messageNode = document.createElement('div')
const attr = document.createAttribute('class')
if (targetHTMLElement) {
attr.value = 'message'
} else {
attr.value = 'message message-body'
}
messageNode.setAttributeNode(attr)
let messageList: HTMLCollectionOf<HTMLElement>
let messageContainerHeight = 0
const offsetTop = 12
if (targetElementId && targetHTMLElement) {
messageList = document
.getElementById(targetElementId)
?.getElementsByClassName('message') as HTMLCollectionOf<HTMLElement>
} else {
messageList = document.getElementsByClassName('message-body') as HTMLCollectionOf<HTMLElement>
}
for (let i = 0; i < messageList.length; i++) {
messageContainerHeight += messageList[i].offsetHeight
}
messageNode.style.top = `${messageContainerHeight + (messageList.length + 1) * offsetTop}px`
// 创建实例并挂载
const app: any = createApp(Message, {
config,
remove() {
handleRemove()
},
})
app.vm = app.mount(messageNode)
if (targetHTMLElement) {
targetHTMLElement.style.position = 'relative'
messageNode.style.position = 'absolute'
targetHTMLElement.appendChild(messageNode)
} else {
document.body.appendChild(messageNode)
}
app.close = () => {
handleRemove()
}
// 取消挂载
const handleRemove = () => {
app.unmount(messageNode)
if (targetHTMLElement) {
targetHTMLElement.removeChild(messageNode)
} else {
document.body.removeChild(messageNode)
}
resetMsgTop()
}
const resetMsgTop = () => {
for (let i = 0; i < messageList.length; i++) {
messageList[i].style.top = `${offsetTop + i * (messageList[i].offsetHeight + offsetTop)}px`
}
}
return app
}
export default createInstance
.message {
position: fixed;
top: 12px;
left: 50%;
z-index: 9999;
max-width: 720px;
padding: 10px 20px;
border-radius: 3px;
box-shadow:
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
transform: translateX(-50%);
}
.slide-fade-enter-active {
transition: all 0.2s ease-out;
}
.slide-fade-leave-active {
transition: all 0.2s ease;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
opacity: 0;
transform: translateY(-20px);
}
import createInstance from './instance.ts'
enum CMessageTypeEnum {
text = 'text',
success = 'success',
error = 'error',
warning = 'warning',
info = 'info',
}
export type CMessageType = 'text' | 'success' | 'error' | 'warning' | 'info'
export interface CMessageOptions {
type: CMessageType // 类型
content: string // 消息内容
icon?: string // 消息图标
duration?: number // 自动关闭延迟时间
close?: boolean // 是否显示关闭按钮
to?: string | 'body' // 挂载位置 仅支持Id选择器 / body
}
function renderMsg(messageType: CMessageType, messageContent: string = '', options?: Partial<CMessageOptions>) {
return new Promise((resolve) => {
const defaultCfg: CMessageOptions = {
type: 'text',
content: '',
icon: '',
duration: 3000,
close: false,
to: 'body',
}
const config: CMessageOptions = Object.assign(
{ ...defaultCfg },
{ type: messageType, content: messageContent },
{ ...options },
)
const { type = 'text', content = '', icon = '', duration = 3000, close = false, to = 'body' } = config
createInstance({
type,
content,
duration,
icon,
close,
to,
})
resolve('')
})
}
export default {
text(content = '', options?: Partial<CMessageOptions>) {
return renderMsg(CMessageTypeEnum.text, content, options)
},
success(content = '', options?: Partial<CMessageOptions>) {
return renderMsg(CMessageTypeEnum.success, content, options)
},
error(content = '', options?: Partial<CMessageOptions>) {
return renderMsg(CMessageTypeEnum.error, content, options)
},
info(content = '', options?: Partial<CMessageOptions>) {
return renderMsg(CMessageTypeEnum.info, content, options)
},
warning(content = '', options?: Partial<CMessageOptions>) {
return renderMsg(CMessageTypeEnum.warning, content, options)
},
}
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { CMessageType, CMessageOptions } from './message'
interface MessageIconItem {
color: string
icon: string
}
interface Props {
config: CMessageOptions
remove: () => void
}
const visible = ref(false)
const props = defineProps<Props>()
const messageIconList: { [k in CMessageType]: MessageIconItem } = {
text: {
icon: '',
color: '#333333',
},
warning: {
icon: 'icon-warning',
color: '#F0A020',
},
info: {
icon: 'icon-info',
color: '#2080F0',
},
error: {
icon: 'icon-error',
color: '#D03050',
},
success: {
icon: 'icon-success',
color: '#18A058',
},
}
onMounted(() => {
handleOpen()
})
function handleOpen() {
visible.value = true
if (props.config.duration !== 0) {
setTimeout(() => {
handleClose()
}, props.config.duration)
}
}
function handleClose() {
visible.value = false
props.remove()
}
</script>
<template>
<transition name="slide-fade">
<div v-show="visible" class="message-container">
<div class="message-content flex items-center">
<div v-if="messageIconList[config.type].icon" class="mr-2.5 h-5 w-5">
<i
class="iconfont flex h-[22px] items-center justify-center text-[20px]"
:class="[messageIconList[config.type].icon]"
:style="{ color: messageIconList[config.type].color }"
/>
</div>
<span class="break-all" v-text="config.content" />
<div
v-if="config.close"
class="hover:bg-background-color rounded-theme ml-2.5 flex h-6 w-6 cursor-pointer items-center justify-center"
@click="handleClose"
>
<i class="iconfont icon-close text-font-color text-sm" />
</div>
</div>
</div>
</transition>
</template>
...@@ -3,8 +3,9 @@ import { computed, ref } from 'vue' ...@@ -3,8 +3,9 @@ import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import { throttle } from 'lodash-es' import { throttle } from 'lodash-es'
import CMessage from './c-message'
import { MessageItemInterface, MultiModelDialogueItem, QuestionMessageItem } from '../types' import { MessageItemInterface, MultiModelDialogueItem, QuestionMessageItem } from '../types'
import { fetchCustomEventSource } from '@/composables/useEventSource' import { fetchEventStreamSource } from '../utils/fetch-event-stream-source'
const { t } = useI18n() const { t } = useI18n()
...@@ -77,7 +78,7 @@ function handleQuestionSubmit() { ...@@ -77,7 +78,7 @@ function handleQuestionSubmit() {
multiModelDialogueList.value.forEach((modelItem, modelIndex) => { multiModelDialogueList.value.forEach((modelItem, modelIndex) => {
const messages: QuestionMessageItem[] = [] const messages: QuestionMessageItem[] = []
const { topP, temperature, agentSystem } = modelItem const { topP, temperature, agentSystem, modelNickName } = modelItem
modelItem.messageList.forEach((messageItem) => { modelItem.messageList.forEach((messageItem) => {
messages.push({ messages.push({
...@@ -111,7 +112,7 @@ function handleQuestionSubmit() { ...@@ -111,7 +112,7 @@ function handleQuestionSubmit() {
modelItem.controller = new AbortController() modelItem.controller = new AbortController()
fetchCustomEventSource({ fetchEventStreamSource({
path: '/api/rest/agentApplicationInfoRest/preview.json', path: '/api/rest/agentApplicationInfoRest/preview.json',
payload: { payload: {
agentId: props.agentId, agentId: props.agentId,
...@@ -119,6 +120,7 @@ function handleQuestionSubmit() { ...@@ -119,6 +120,7 @@ function handleQuestionSubmit() {
topP, topP,
temperature, temperature,
agentSystem, agentSystem,
modelNickName,
}, },
controller: modelItem.controller, controller: modelItem.controller,
onMessage: (data: any) => { onMessage: (data: any) => {
...@@ -150,6 +152,9 @@ function handleQuestionSubmit() { ...@@ -150,6 +152,9 @@ function handleQuestionSubmit() {
onRequestError: () => { onRequestError: () => {
errorMessageResponse(questionMessageId, answerMessageId, modelIndex) errorMessageResponse(questionMessageId, answerMessageId, modelIndex)
}, },
onError: (err: any) => {
CMessage.error(err.message || '', { to: `#${modelItem.id}` })
},
onFinally: () => { onFinally: () => {
modelItem.controller = null modelItem.controller = null
modelItem.isAnswerResponseWait = false modelItem.isAnswerResponseWait = false
......
...@@ -88,7 +88,10 @@ function scrollToBottom() { ...@@ -88,7 +88,10 @@ function scrollToBottom() {
</script> </script>
<template> <template>
<div class="border-inactive-border-color flex flex-col overflow-hidden border-r px-6 last-of-type:border-none"> <div
:id="modelDialogueItem.id"
class="border-inactive-border-color flex flex-col overflow-hidden border-r px-6 last-of-type:border-none"
>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-5"> <div class="flex items-center gap-5">
<n-popover <n-popover
......
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { BASE_URLS } from '@/config/base-url'
import { useUserStore } from '@/store/modules/user'
const EVENT_SOURCE_BASE_URL = `${BASE_URLS[window.ENV || 'DEV']}`
export function fetchEventStreamSource(config: {
path: string
payload: any
controller: AbortController
onMessage: (data: string) => void
onRequestError: (err: any) => void
onError?: (err: any) => void
onFinally?: () => void
}) {
const userStore = useUserStore()
let responseError = false
fetchEventSource(`${EVENT_SOURCE_BASE_URL}${config.path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Request-Token': userStore.token || '',
},
body: JSON.stringify(config.payload || {}),
signal: config.controller?.signal,
openWhenHidden: true,
onmessage: (e) => {
if (e.data === '[DONE]' && !responseError) {
config.onMessage(e.data)
config.onFinally && config.onFinally()
return
}
try {
const data = JSON.parse(e.data)
if (data.code === -10) {
window.$message.info('身份已过期,请重新登陆')
config.onError && config.onError(data)
userStore.logout()
return
}
if (data.code === -1) {
responseError = true
config.controller?.abort()
config.onFinally && config.onFinally()
config.onError && config.onError(data)
return
}
config.onMessage(data.message)
} catch (err) {
config.onRequestError(err)
config.onFinally && config.onFinally()
}
},
onclose: () => {},
onerror: (err) => {
config.onRequestError(err)
config.onFinally && config.onFinally()
throw err
},
})
}
...@@ -23,8 +23,6 @@ const emit = defineEmits<Emits>() ...@@ -23,8 +23,6 @@ const emit = defineEmits<Emits>()
const autoConfigInputValue = ref('') const autoConfigInputValue = ref('')
const autoConfigInputType = ref<'random' | 'input'>('random')
const showModal = computed({ const showModal = computed({
get() { get() {
return props.isShowModal return props.isShowModal
...@@ -38,12 +36,8 @@ const isDisabledBtn = computed(() => { ...@@ -38,12 +36,8 @@ const isDisabledBtn = computed(() => {
return !autoConfigInputValue.value return !autoConfigInputValue.value
}) })
const isRandomBtnLoading = computed(() => {
return props.btnLoading && autoConfigInputType.value === 'random'
})
const isInputBtnLoading = computed(() => { const isInputBtnLoading = computed(() => {
return props.btnLoading && autoConfigInputType.value === 'input' return props.btnLoading
}) })
watch( watch(
...@@ -57,14 +51,23 @@ function handleCloseModal() { ...@@ -57,14 +51,23 @@ function handleCloseModal() {
emit('update:isShowModal', false) emit('update:isShowModal', false)
} }
function handleConfirm(inputType: 'random' | 'input') { function handleConfirm() {
autoConfigInputType.value = inputType emit('confirm', autoConfigInputValue.value)
emit('confirm', inputType === 'random' ? '' : autoConfigInputValue.value)
} }
</script> </script>
<template> <template>
<CustomModal v-model:is-show="showModal" :title="modalTitle" :width="600"> <CustomModal
v-model:is-show="showModal"
:title="modalTitle"
:width="600"
:btn-disabled="isDisabledBtn"
:btn-loading="isInputBtnLoading"
:cancel-btn-text="t('common_module.cancel_btn_text')"
:confirm-btn-text="t('common_module.ai_generate')"
@close="handleCloseModal"
@confirm="handleConfirm"
>
<template #content> <template #content>
<div class="mb-3 flex h-8 w-full items-center rounded bg-[#FFF4E6] px-4"> <div class="mb-3 flex h-8 w-full items-center rounded bg-[#FFF4E6] px-4">
<CustomIcon icon="ep:warning-filled" class="mr-2 h-4 w-4 text-[#FFA500]" /> <CustomIcon icon="ep:warning-filled" class="mr-2 h-4 w-4 text-[#FFA500]" />
...@@ -89,37 +92,5 @@ function handleConfirm(inputType: 'random' | 'input') { ...@@ -89,37 +92,5 @@ function handleConfirm(inputType: 'random' | 'input') {
class="rounded-lg!" class="rounded-lg!"
/> />
</template> </template>
<template #footer>
<div class="flex w-full items-center justify-end">
<NButton class="h-[32px]! rounded-md! px-6!" @click="handleCloseModal">
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_config_module.auto_config_modal_module.cancel_btn_text',
)
}}
</NButton>
<NButton
:loading="isRandomBtnLoading"
class="h-[32px]! rounded-md! px-6! ml-4!"
@click="handleConfirm('random')"
>
{{
t(
'personal_space_module.agent_module.agent_setting_module.agent_config_module.auto_config_modal_module.random_generate_btn_text',
)
}}
</NButton>
<NButton
:loading="isInputBtnLoading"
type="primary"
:disabled="isDisabledBtn"
class="h-[32px]! px-6! rounded-md! ml-4!"
@click="handleConfirm('input')"
>
{{ t('common_module.ai_generate') }}
</NButton>
</div>
</template>
</CustomModal> </CustomModal>
</template> </template>
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import { computed, inject, onUnmounted, ref } from 'vue' import { computed, inject, onUnmounted, ref } from 'vue'
import { Emitter } from 'mitt' import { Emitter } from 'mitt'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { fetchCustomEventSource } from '@/composables/useEventSource' import { fetchCustomEventSource } from '@/composables/useEventSource'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config' import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
...@@ -205,9 +204,8 @@ defineExpose({ ...@@ -205,9 +204,8 @@ defineExpose({
<div class="mr-2 flex h-8 w-8 items-center justify-center"> <div class="mr-2 flex h-8 w-8 items-center justify-center">
<NPopover trigger="hover"> <NPopover trigger="hover">
<template #trigger> <template #trigger>
<CustomIcon <i
icon="fluent:delete-12-regular" class="iconfont icon-clear text-base leading-none"
class="text-base outline-none"
:class=" :class="
isAllowClearMessage isAllowClearMessage
? 'hover:text-theme-color cursor-pointer text-[#5c5f66]' ? 'hover:text-theme-color cursor-pointer text-[#5c5f66]'
......
...@@ -114,7 +114,7 @@ async function handleGetKnowledgeChunkList() { ...@@ -114,7 +114,7 @@ async function handleGetKnowledgeChunkList() {
} }
function handleBackKnowledgeDocumentList() { function handleBackKnowledgeDocumentList() {
router.back() router.replace({ name: 'KnowledgeDocument', params: { id: router.currentRoute.value.params.id } })
} }
async function handleSearchKnowledgeChunkList() { async function handleSearchKnowledgeChunkList() {
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import { computed, inject, onMounted, onUnmounted, ref } from 'vue' import { computed, inject, onMounted, onUnmounted, ref } from 'vue'
import { Emitter } from 'mitt' import { Emitter } from 'mitt'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { fetchCustomEventSource } from '@/composables/useEventSource' import { fetchCustomEventSource } from '@/composables/useEventSource'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { useLayoutConfig } from '@/composables/useLayoutConfig' import { useLayoutConfig } from '@/composables/useLayoutConfig'
...@@ -206,9 +205,8 @@ defineExpose({ ...@@ -206,9 +205,8 @@ defineExpose({
<div class="mr-2 flex items-center justify-center" :class="isMobile ? 'h-6 w-6' : 'h-8 w-8'"> <div class="mr-2 flex items-center justify-center" :class="isMobile ? 'h-6 w-6' : 'h-8 w-8'">
<NPopover trigger="hover"> <NPopover trigger="hover">
<template #trigger> <template #trigger>
<CustomIcon <i
icon="fluent:delete-12-regular" class="iconfont icon-clear text-base leading-none"
class="text-base outline-none"
:class=" :class="
isAllowClearMessage isAllowClearMessage
? 'hover:text-theme-color cursor-pointer text-[#5c5f66]' ? 'hover:text-theme-color cursor-pointer text-[#5c5f66]'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment