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>
|