样式修改

This commit is contained in:
2025-12-09 16:11:17 +08:00
parent a063e9ce32
commit 68cbf797f1

View File

@@ -1,8 +1,14 @@
<template>
<div v-if="hasAgent">
<!-- 悬浮球 -->
<div class="ball-container">
<div class="chat-ball" @click="openDrawer">
<div
v-show="!drawerVisible"
class="ball-container"
:style="ballStyle"
@mousedown="startDrag"
@touchstart="startDrag"
>
<div class="chat-ball" @click="handleBallClick">
<img src="@/assets/imgs/chat-ball.svg" alt="AI助手" class="ball-icon" />
<div v-if="unreadCount > 0" class="unread-badge">{{ unreadCount }}</div>
</div>
@@ -134,7 +140,7 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, nextTick } from 'vue';
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Close, Paperclip, VideoPause, Promotion } from '@element-plus/icons-vue';
import { chatApi, chatHistoryApi, aiAgentConfigApi, fileUploadApi } from '@/apis/ai';
@@ -160,15 +166,27 @@ const agentAvatarUrl = ref<string>('');
// ===== 悬浮球相关 =====
const ballRef = ref<HTMLElement | null>(null);
const ballX = ref(0);
const ballX = ref(0); // 实际位置(像素)
const ballY = ref(0);
const unreadCount = ref(0);
const isDragging = ref(false);
const dragStartX = ref(0);
const dragStartY = ref(0);
// 存储悬浮球的相对位置百分比用于窗口resize时保持相对位置
const ballXPercent = ref(1); // 1 表示右侧
const ballYPercent = ref(0.85); // 0.85 表示距离底部较近
const isUserDragged = ref(false); // 标记用户是否手动拖动过
// 拖拽检测相关
const dragStartPosX = ref(0); // 记录拖拽开始时的实际位置
const dragStartPosY = ref(0);
// 悬浮球样式
const ballStyle = computed(() => ({
right: '20px',
bottom: '100px',
position: 'fixed'
left: `${ballX.value}px`,
top: `${ballY.value}px`,
position: 'fixed' as const
}));
// 打开抽屉
@@ -176,7 +194,133 @@ function openDrawer() {
drawerVisible.value = true;
}
// ===== 对话相关 =====
// 开始拖拽
function startDrag(event: MouseEvent | TouchEvent) {
isDragging.value = true;
const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
dragStartX.value = clientX - ballX.value;
dragStartY.value = clientY - ballY.value;
// 记录起始位置,用于判断是点击还是拖拽
dragStartPosX.value = ballX.value;
dragStartPosY.value = ballY.value;
// 添加事件监听
document.addEventListener('mousemove', onDrag);
document.addEventListener('touchmove', onDrag, { passive: false });
document.addEventListener('mouseup', endDrag);
document.addEventListener('touchend', endDrag);
event.preventDefault();
}
// 结束拖拽
function endDrag() {
if (!isDragging.value) return;
// 移除事件监听
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('mouseup', endDrag);
document.removeEventListener('touchmove', onDrag);
document.removeEventListener('touchend', endDrag);
// 计算移动距离
const moveDistanceX = Math.abs(ballX.value - dragStartPosX.value);
const moveDistanceY = Math.abs(ballY.value - dragStartPosY.value);
const totalDistance = Math.sqrt(moveDistanceX * moveDistanceX + moveDistanceY * moveDistanceY);
// 判断是点击还是拖拽移动距离阈值为5px
const isClick = totalDistance <= 5;
if (isClick) {
// 如果是点击,打开抽屉
openDrawer();
} else {
// 如果是拖拽,执行吸附和位置调整
isUserDragged.value = true;
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const ballWidth = 40;
const ballHeight = 40;
const margin = 16;
// 自动吸附到左右两侧
if (ballX.value < windowWidth / 2) {
// 吸附到左侧
ballX.value = margin;
ballXPercent.value = 0;
} else {
// 吸附到右侧
ballX.value = windowWidth - ballWidth - margin;
ballXPercent.value = 1;
}
// 限制垂直位置并保存百分比
if (ballY.value < margin) {
ballY.value = margin;
} else if (ballY.value > windowHeight - ballHeight - margin) {
ballY.value = windowHeight - ballHeight - margin;
}
// 保存Y位置的百分比以中心点计算
ballYPercent.value = (ballY.value + ballHeight / 2) / windowHeight;
}
isDragging.value = false;
}
// 拖拽过程
function onDrag(event: MouseEvent | TouchEvent) {
if (!isDragging.value) return;
const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX;
const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY;
ballX.value = clientX - dragStartX.value;
ballY.value = clientY - dragStartY.value;
event.preventDefault();
}
// 根据百分比计算实际位置
function updateBallPosition() {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const ballWidth = 40;
const ballHeight = 40;
const margin = 16;
// 根据百分比计算位置
if (ballXPercent.value < 0.5) {
// 左侧
ballX.value = margin;
} else {
// 右侧
ballX.value = windowWidth - ballWidth - margin;
}
// 计算Y位置确保不超出边界
let targetY = windowHeight * ballYPercent.value - ballHeight / 2;
targetY = Math.max(margin, Math.min(targetY, windowHeight - ballHeight - margin));
ballY.value = targetY;
}
// 窗口resize监听器
function handleResize() {
updateBallPosition();
}
// 处理悬浮球点击(区分点击和拖拽)
function handleBallClick() {
if (isDragging.value) return; // 拖动过程中不触发
openDrawer();
}
// 对话相关
const conversations = ref<AiConversation[]>([]);
const currentConversation = ref<AiConversation | null>(null);
const hasMoreConversations = ref(false);
@@ -447,9 +591,20 @@ function formatMarkdown(content: string) {
// 生命周期
onMounted(() => {
// 初始化悬浮球位置
updateBallPosition();
// 监听窗口resize事件
window.addEventListener('resize', handleResize);
loadAgentConfig();
loadRecentConversations();
});
// 清理监听器
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped lang="scss">
@@ -459,11 +614,15 @@ onMounted(() => {
right: 16px;
bottom: 80px;
z-index: 9999;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.chat-ball {
width: 56px;
height: 56px;
width: 40px;
height: 40px;
cursor: pointer;
position: relative;
transition: transform 0.3s ease;