Skip to content

Commit 86d2853

Browse files
committed
fix(chat-ui): 修复右键菜单越界和上下文模式状态丢失
- 为聊天右键菜单增加视口内重定位,避免菜单显示到屏幕外 - 在异步加载编辑状态后重新计算位置,并限制菜单高度支持滚动 - 补回 searchContext 状态传递,恢复上下文模式下的筛选禁用和提示横幅
1 parent 87b263e commit 86d2853

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

frontend/components/chat/ChatOverlays.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,8 @@
10861086

10871087
<div
10881088
v-if="contextMenu.visible"
1089-
class="fixed z-[12000] bg-white border border-gray-200 rounded-md shadow-lg text-sm"
1089+
ref="contextMenuElement"
1090+
class="fixed z-[12000] max-h-[calc(100vh-16px)] overflow-y-auto bg-white border border-gray-200 rounded-md shadow-lg text-sm"
10901091
:style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
10911092
@click.stop
10921093
>

frontend/composables/chat/useChatEditing.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { ref, toRaw } from 'vue'
1+
import { nextTick, ref, toRaw } from 'vue'
2+
3+
const CONTEXT_MENU_MARGIN = 8
24

35
const initialContextMenu = () => ({
46
visible: false,
@@ -45,13 +47,52 @@ export const useChatEditing = ({
4547
locateMessageByServerId
4648
}) => {
4749
const contextMenu = ref(initialContextMenu())
50+
const contextMenuElement = ref(null)
4851
const messageEditModal = ref(initialMessageEditModal())
4952
const messageFieldsModal = ref(initialMessageFieldsModal())
5053

5154
const closeContextMenu = () => {
5255
contextMenu.value = initialContextMenu()
5356
}
5457

58+
const repositionContextMenu = () => {
59+
if (!process.client || !contextMenu.value.visible) return
60+
const menuEl = contextMenuElement.value
61+
if (!menuEl) return
62+
63+
const rect = menuEl.getBoundingClientRect()
64+
const viewportWidth = Math.max(window.innerWidth || 0, document.documentElement?.clientWidth || 0)
65+
const viewportHeight = Math.max(window.innerHeight || 0, document.documentElement?.clientHeight || 0)
66+
if (!viewportWidth || !viewportHeight) return
67+
68+
const maxX = Math.max(CONTEXT_MENU_MARGIN, viewportWidth - rect.width - CONTEXT_MENU_MARGIN)
69+
const maxY = Math.max(CONTEXT_MENU_MARGIN, viewportHeight - rect.height - CONTEXT_MENU_MARGIN)
70+
const currentX = Number(contextMenu.value.x || 0)
71+
const currentY = Number(contextMenu.value.y || 0)
72+
const nextX = Math.min(Math.max(currentX, CONTEXT_MENU_MARGIN), maxX)
73+
const nextY = Math.min(Math.max(currentY, CONTEXT_MENU_MARGIN), maxY)
74+
75+
if (nextX !== currentX || nextY !== currentY) {
76+
contextMenu.value = {
77+
...contextMenu.value,
78+
x: nextX,
79+
y: nextY
80+
}
81+
}
82+
}
83+
84+
const scheduleContextMenuReposition = () => {
85+
if (!process.client) return
86+
void nextTick(() => {
87+
const run = () => repositionContextMenu()
88+
if (typeof window.requestAnimationFrame === 'function') {
89+
window.requestAnimationFrame(run)
90+
} else {
91+
run()
92+
}
93+
})
94+
}
95+
5596
const loadContextMenuEditStatus = async (params) => {
5697
if (!process.client) return
5798
const account = String(params?.account || '').trim()
@@ -67,16 +108,19 @@ export const useChatEditing = ({
67108
const current = String(contextMenu.value?.message?.id || '').trim()
68109
if (contextMenu.value.visible && current === messageId) {
69110
contextMenu.value.editStatus = response || { modified: false }
111+
scheduleContextMenuReposition()
70112
}
71113
} catch {
72114
const current = String(contextMenu.value?.message?.id || '').trim()
73115
if (contextMenu.value.visible && current === messageId) {
74116
contextMenu.value.editStatus = null
117+
scheduleContextMenuReposition()
75118
}
76119
} finally {
77120
const current = String(contextMenu.value?.message?.id || '').trim()
78121
if (contextMenu.value.visible && current === messageId) {
79122
contextMenu.value.editStatusLoading = false
123+
scheduleContextMenuReposition()
80124
}
81125
}
82126
}
@@ -126,6 +170,8 @@ export const useChatEditing = ({
126170
void loadContextMenuEditStatus({ account, username, message_id: messageId })
127171
}
128172
} catch {}
173+
174+
scheduleContextMenuReposition()
129175
}
130176

131177
const prettyJson = (value) => {
@@ -519,6 +565,7 @@ export const useChatEditing = ({
519565

520566
return {
521567
contextMenu,
568+
contextMenuElement,
522569
messageEditModal,
523570
messageFieldsModal,
524571
closeContextMenu,

frontend/pages/chat/[[username]].vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ const chatState = {
502502
availableAccounts,
503503
contacts,
504504
selectedContact,
505+
searchContext,
505506
filteredContacts,
506507
searchQuery,
507508
showSearchAccountSwitcher,

0 commit comments

Comments
 (0)