# 实现拖拽控制页面宽度
// 拖拽实现
const isDragging = ref(false)
const QA_WIDTH = 816 // 问答区域最小宽度
let startX = 0 // 按下时的起始坐标
let startWidth = 0
let maxWidth = 0
let minWidth = defaultHistoryRecordsWidth
const startDrag = (e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
const container = document.querySelector('.contain') as HTMLElement
const containerWidth = container ? container.clientWidth : window.innerWidth
maxWidth = containerWidth - QA_WIDTH
// 如果已经在拖拽中,忽略新的拖拽事件
if (isDragging.value) return
isDragging.value = true
startX = e.clientX
startWidth = historyRecordsWidth.value
document.addEventListener('mousemove', onDrag)
document.addEventListener('mouseup', stopDrag)
document.addEventListener('mouseleave', stopDrag)
}
function onDrag(e: MouseEvent) {
const delta = startX - e.clientX
let newWidth = startWidth + delta
if (newWidth < minWidth) newWidth = minWidth
if (newWidth > maxWidth) newWidth = maxWidth
historyRecordsWidth.value = newWidth
}
function stopDrag() {
isDragging.value = false
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
document.removeEventListener('mouseleave', stopDrag)
localStorage.setItem('historyRecordsWidth', historyRecordsWidth.value.toString())
}
const cleanupDrag = () => {
startX = 0
startWidth = 0
isDragging.value = false
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
document.removeEventListener('mouseleave', stopDrag)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 需要注意的坑
浏览器事件模型的常见坑:
当鼠标拖拽经过 <iframe> 边界时,mousemove / mouseup 事件会“丢失”,因为鼠标进入了 另一个文档的上下文,原页面不再接收事件,所以拖拽逻辑就“卡住”了。
解决方案:
# 方案 1:在 iframe 内部也监听(同域才可行)
如果 iframe 和父页面是同域,可以:
const iframe = document.querySelector('iframe') as HTMLIFrameElement
iframe.onload = () => {
const iframeDoc = iframe.contentDocument!
iframeDoc.addEventListener('mousemove', onDrag)
iframeDoc.addEventListener('mouseup', stopDrag)
}
1
2
3
4
5
6
2
3
4
5
6
这样鼠标进入 iframe 时,依然能触发内部的事件。
⚠️ 限制:必须同源(协议、域名、端口都相同)。
# 方案 2:拖拽时加一层「透明遮罩」(推荐 ✅)
核心思路:开始拖拽时,在页面最上层放一个 div 覆盖整个窗口,
即使鼠标移动到 iframe 区域,实际上仍然停留在这个透明层之上,
因此所有 mousemove / mouseup 事件都仍由父页面捕获。
<template>
<div
v-if="previewDocShow"
:class="`history-records ${isfullScreen && 'history-records-full'} ${
isDragging && 'dragging'
}`"
:style="`width: ${historyRecordsWidth}px`"
>
<!-- 拖拽遮罩 -->
<div v-if="isDragging" class="drag-overlay"></div>
<!-- 拖拽调整宽度控制 -->
<div
id="drag-resize"
class="drag-resize"
:title="`拖拽调整宽度 (当前: ${historyRecordsWidth}px)`"
@mousedown="startDrag"
>
⋮
</div>
</div>
</template>
<style scoped>
.drag-overlay {
position: absolute;
inset: 0;
z-index: 98; // 低于拖拽手柄(99),高于内部 iframe
background: transparent;
cursor: ew-resize;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
优点:
- 不需要访问 iframe。
- 适用跨域 iframe。
- 简单可靠。
drag-overlay的样式作用如下:
.drag-overlay {
position: fixed; /* 位置固定在视口,不随滚动条变化 */
inset: 0; /* 等价于 top:0; right:0; bottom:0; left:0; 让元素充满整个窗口 */
z-index: 9999; /* 放到页面最上层,保证盖住所有内容和 iframe */
cursor: col-resize; /* 鼠标样式显示为左右拖拽(左右两向箭头) */
background: transparent; /* 完全透明,看不到但仍然接收鼠标事件 */
}
1
2
3
4
5
6
7
2
3
4
5
6
7