Files
FiDA_Front/src/components/Assistant/assistant.vue

228 lines
4.7 KiB
Vue
Raw Normal View History

2026-03-05 17:12:35 +08:00
<template>
2026-03-31 11:05:07 +08:00
<div
class="trigger flex flex-center"
:class="{ 'has-unread': hasUnread }"
ref="triggerRef"
@mousedown="handleTriggerMouseDown"
@click="handleTriggerClick"
>
<img
src="@/assets/images/assistant-trigger.png"
class="trigger-icon"
alt=""
@dragstart.prevent
/>
</div>
<div
v-show="showAssistant"
class="assistant-container flex flex-col space-between"
ref="containerRef"
>
<div
class="assistant-header flex space-between align-center"
@mousedown="handleContainerMouseDown"
>
2026-03-05 17:12:35 +08:00
<div class="assistant-header-title">AI Assistant</div>
<SvgIcon name="canvas-assistant-menu" class="menu-icon" color="#D58C4D" />
</div>
<div class="assistant-body flex-1">
<List :messageList="messageList" />
</div>
2026-03-31 13:17:03 +08:00
<!-- <div class="assistant-input flex align-center">
2026-03-05 17:12:35 +08:00
<el-input v-model="inputMessage" :placeholder="$t('assistant.inputPlaceholder')" />
<div class="sender-btn flex flex-center" @click="handleSendAgent">
<img src="@/assets/images/sender.png" class="sender-icon" />
</div>
2026-03-31 13:17:03 +08:00
</div> -->
2026-03-05 17:12:35 +08:00
</div>
</template>
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
<script setup lang="ts">
2026-03-31 11:05:07 +08:00
import { ref, onMounted, watch } from 'vue'
2026-03-05 17:12:35 +08:00
import List from './component/List.vue'
import { useDraggable } from './component/useDraggable'
2026-03-31 11:05:07 +08:00
import MyEvent from '@/utils/myEvent'
interface Message {
nodeType: string
content: string
}
2026-03-05 17:12:35 +08:00
const inputMessage = ref('')
const containerRef = ref<HTMLElement>()
2026-03-31 11:05:07 +08:00
const triggerRef = ref<HTMLElement>()
const { handleMouseDown: handleContainerMouseDown } = useDraggable(containerRef)
const {
handleMouseDown: handleTriggerMouseDown,
consumeClickSuppression: consumeTriggerClickSuppression
} = useDraggable(triggerRef, {
boundary: 'parent'
})
2026-03-05 17:12:35 +08:00
2026-03-31 11:05:07 +08:00
const showAssistant = ref(false)
const handleTriggerClick = () => {
if (consumeTriggerClickSuppression()) {
return
}
showAssistant.value = !showAssistant.value
}
const messageList = ref<Message[]>([])
const hasUnread = ref(false)
watch(
() => showAssistant.value,
(newVal) => {
if (newVal) {
hasUnread.value = false
}
2026-03-05 17:12:35 +08:00
}
2026-03-31 11:05:07 +08:00
)
const listenAssistantPushChat = (message: Message) => {
// console.log('有新消息--');
const exist = messageList.value.find((item: Message) => item.nodeType === message.nodeType)
if (!exist) {
messageList.value.push(message)
hasUnread.value = true
}
}
onMounted(() => {
MyEvent.add('assistantPushChat', listenAssistantPushChat)
})
2026-03-05 17:12:35 +08:00
</script>
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
<style lang="less" scoped>
2026-03-31 11:05:07 +08:00
.trigger {
position: absolute;
right: 0;
width: 5rem;
height: 5rem;
bottom: 50%;
background-color: #fff;
border-radius: 50%;
cursor: grab;
user-select: none;
box-shadow: 0px 13.86px 19.43px 0px #0000000d;
border: 0.18rem solid #ebebeb;
&:active {
cursor: grabbing;
}
&.has-unread {
&::after {
content: '';
position: absolute;
top: 0.5rem;
right: 0.4rem;
width: 0.6rem;
height: 0.6rem;
background-color: #ff4747;
border-radius: 50%;
}
}
.trigger-icon {
width: 2.7rem;
height: 2.7rem;
pointer-events: none;
}
}
2026-03-05 17:12:35 +08:00
.assistant-container {
position: absolute;
right: 6.2rem;
bottom: 13.2rem;
width: 46.69rem;
height: 56.6rem;
flex-shrink: 0;
2026-03-27 09:36:21 +08:00
background-color: #fff;
2026-03-05 17:12:35 +08:00
box-shadow: 0px 19.44px 27.22px 0px #0000000d;
border: 1px solid;
border-image-source: linear-gradient(
119.03deg,
rgba(233, 121, 60, 0.3) 1.61%,
rgba(255, 207, 144, 0.3) 101.01%
);
border-radius: 1.69rem;
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.assistant-header {
height: 5.66rem;
font-family: 'Regular';
font-size: 1.6rem;
padding: 0 1.59rem 0 2.05rem;
background: linear-gradient(
119.03deg,
rgba(233, 121, 60, 0.3) 1.61%,
rgba(255, 207, 144, 0.3) 101.01%
);
border-top-left-radius: 1.69rem;
border-top-right-radius: 1.69rem;
user-select: none;
cursor: grabbing;
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
&:active {
cursor: grabbing;
}
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.menu-icon {
width: 1.8rem;
}
}
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.assistant-body {
margin: 1.6rem 0 2.9rem;
padding: 0 1.95rem 0 2.5rem;
overflow-y: auto;
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
&::-webkit-scrollbar {
width: 0;
display: none;
}
&::-webkit-scrollbar-thumb {
display: none;
}
&::-webkit-scrollbar-track {
display: none;
}
}
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.assistant-input {
border: 1px solid #0000001a;
margin: 0 2rem 3rem;
border-radius: 3.53rem;
padding: 0 1rem 0 1.7rem;
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.el-input {
:deep(.el-input__wrapper) {
background-color: transparent;
box-shadow: none;
padding: 0;
}
}
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.sender-btn {
width: 2.2rem;
height: 2.2rem;
cursor: pointer;
background-color: #000;
border-radius: 50%;
.sender-icon {
width: 0.9rem;
height: 0.9rem;
}
2026-03-31 11:05:07 +08:00
2026-03-05 17:12:35 +08:00
.sender-pause {
width: 1rem;
height: 1rem;
background-color: #fff;
}
}
}
}
</style>