|
@@ -1,19 +1,99 @@
|
|
|
<template>
|
|
|
- <div class="chat-float-button" @click="handleClick">
|
|
|
+ <div
|
|
|
+ class="chat-float-button"
|
|
|
+ @mousedown="onDragStart"
|
|
|
+ @click="onButtonClick"
|
|
|
+ >
|
|
|
<img class="chat-icon" src="@/assets/chat/chat_line.png" alt="在线会话" />
|
|
|
- <span class="chat-label">在线会话</span>
|
|
|
+ <span class="chat-label" @click.stop="onLabelClick">在线会话</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+let dragSuppressClick = false;
|
|
|
export default {
|
|
|
name: 'ChatFloatButton',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ isDragging: false,
|
|
|
+ fixedWidth: null,
|
|
|
+ fixedHeight: null,
|
|
|
+ };
|
|
|
+ },
|
|
|
methods: {
|
|
|
- handleClick() {
|
|
|
- this.$emit('open');
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+ onLabelClick(e) {
|
|
|
+ if (!dragSuppressClick) {
|
|
|
+ this.$emit('open');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onButtonClick(e) {
|
|
|
+ if (dragSuppressClick) {
|
|
|
+ dragSuppressClick = false;
|
|
|
+ e.stopImmediatePropagation && e.stopImmediatePropagation();
|
|
|
+ e.stopPropagation();
|
|
|
+ e.preventDefault();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onDragStart(e) {
|
|
|
+ if (e.button !== 0) return;
|
|
|
+ const btn = this.$el;
|
|
|
+ const rect = btn.getBoundingClientRect();
|
|
|
+ const startX = e.clientX;
|
|
|
+ const startY = e.clientY;
|
|
|
+ const startLeft = rect.left;
|
|
|
+ const startTop = rect.top;
|
|
|
+ const width = rect.width;
|
|
|
+ const height = rect.height;
|
|
|
+
|
|
|
+ // 固定宽高,拖动过程中始终不变
|
|
|
+ btn.style.width = width + 'px';
|
|
|
+ btn.style.height = height + 'px';
|
|
|
+ this.fixedWidth = width;
|
|
|
+ this.fixedHeight = height;
|
|
|
+
|
|
|
+ btn.style.left = startLeft + 'px';
|
|
|
+ btn.style.top = startTop + 'px';
|
|
|
+ btn.style.right = '';
|
|
|
+ btn.style.bottom = '';
|
|
|
+ btn.style.position = 'fixed';
|
|
|
+ btn.style.cursor = 'move';
|
|
|
+ document.body.style.userSelect = 'none';
|
|
|
+
|
|
|
+ let moved = false;
|
|
|
+
|
|
|
+ const onMouseMove = (moveEvent) => {
|
|
|
+ const dx = moveEvent.clientX - startX;
|
|
|
+ const dy = moveEvent.clientY - startY;
|
|
|
+ if (!moved && (Math.abs(dx) > 2 || Math.abs(dy) > 2)) {
|
|
|
+ moved = true;
|
|
|
+ }
|
|
|
+ let newLeft = startLeft + dx;
|
|
|
+ let newTop = startTop + dy;
|
|
|
+ const maxLeft = window.innerWidth - width;
|
|
|
+ const maxTop = window.innerHeight - height;
|
|
|
+ newLeft = Math.max(0, Math.min(newLeft, maxLeft));
|
|
|
+ newTop = Math.max(0, Math.min(newTop, maxTop));
|
|
|
+ btn.style.left = newLeft + 'px';
|
|
|
+ btn.style.top = newTop + 'px';
|
|
|
+ };
|
|
|
+
|
|
|
+ const onMouseUp = () => {
|
|
|
+ document.removeEventListener('mousemove', onMouseMove);
|
|
|
+ document.removeEventListener('mouseup', onMouseUp);
|
|
|
+ document.body.style.userSelect = '';
|
|
|
+ btn.style.cursor = 'pointer';
|
|
|
+ // 拖动结束后,宽高依然保持固定,不恢复自适应
|
|
|
+ // btn.style.width/height 保持不变
|
|
|
+ if (moved) {
|
|
|
+ dragSuppressClick = true;
|
|
|
+ setTimeout(() => { dragSuppressClick = false; }, 100);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ document.addEventListener('mousemove', onMouseMove);
|
|
|
+ document.addEventListener('mouseup', onMouseUp);
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
@@ -30,17 +110,16 @@ export default {
|
|
|
cursor: pointer;
|
|
|
z-index: 9999;
|
|
|
border: 1px solid #eee;
|
|
|
+ /* 不设置 width/height,JS 控制 */
|
|
|
}
|
|
|
.chat-icon {
|
|
|
- width: 32px;
|
|
|
- height: 32px;
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
margin-right: 8px;
|
|
|
- border-radius: 50%;
|
|
|
- border: 1px solid #e0e0e0;
|
|
|
- object-fit: cover;
|
|
|
}
|
|
|
.chat-label {
|
|
|
font-size: 16px;
|
|
|
color: #333;
|
|
|
+ user-select: none;
|
|
|
}
|
|
|
</style>
|