This commit is contained in:
hrx 2026-04-27 10:27:48 +08:00
parent f31a47f56c
commit 22204bedec

View File

@ -160,8 +160,13 @@
</div>
</div>
<div v-show="aiDialogVisible" class="ai-chat-panel">
<div class="ai-chat-panel__header">
<div
v-show="aiDialogVisible"
ref="aiChatPanel"
class="ai-chat-panel"
:style="aiPanelStyle"
>
<div class="ai-chat-panel__header" @mousedown="startAIPanelDrag">
<div class="ai-chat-panel__title">
<span class="ai-chat-panel__logo">AI</span>
<span>有问题找开元</span>
@ -260,6 +265,15 @@ export default Vue.extend({
aiInput: '',
aiLoading: false,
aiMessages: [],
aiPanelPosition: {
left: null,
top: null
},
aiPanelDragging: false,
aiPanelDragOffset: {
x: 0,
y: 0
},
aiQuickQuestions: [
'推荐适合训练大模型的 GPU 服务器',
'4090 和 A100 怎么选',
@ -308,6 +322,9 @@ export default Vue.extend({
}
});
},
beforeDestroy() {
this.stopAIPanelDrag()
},
computed: {
...mapGetters(["sidebar", "avatar", "device"]),
...mapState({
@ -342,6 +359,18 @@ export default Vue.extend({
} else {
return this.$route.path.includes('/homePage/index');
}
},
aiPanelStyle() {
if (this.aiPanelPosition.left === null || this.aiPanelPosition.top === null) {
return {}
}
return {
left: `${this.aiPanelPosition.left}px`,
top: `${this.aiPanelPosition.top}px`,
right: 'auto',
bottom: 'auto'
}
}
},
methods: {
@ -350,12 +379,14 @@ export default Vue.extend({
handleAIClick() {
this.aiDialogVisible = true
this.$nextTick(() => {
this.initAIPanelPosition()
this.scrollAIChatToBottom()
})
},
closeAIPanel() {
this.aiDialogVisible = false
this.stopAIPanelDrag()
},
resetAIChat() {
@ -367,6 +398,73 @@ export default Vue.extend({
this.sendAIMessage(question)
},
initAIPanelPosition() {
if (this.aiPanelPosition.left !== null && this.aiPanelPosition.top !== null) {
return
}
const panel = this.$refs.aiChatPanel
if (!panel) return
const panelWidth = panel.offsetWidth || 380
const panelHeight = panel.offsetHeight || 620
this.aiPanelPosition = {
left: Math.max(window.innerWidth - panelWidth - 24, 16),
top: Math.max(window.innerHeight - panelHeight - 24, 16)
}
},
startAIPanelDrag(event) {
if (event.button !== 0) return
if (event.target && event.target.closest('.ai-chat-panel__close')) {
return
}
const panel = this.$refs.aiChatPanel
if (!panel) return
const rect = panel.getBoundingClientRect()
this.aiPanelDragging = true
this.aiPanelDragOffset = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
window.addEventListener('mousemove', this.handleAIPanelDrag)
window.addEventListener('mouseup', this.stopAIPanelDrag)
document.body.style.userSelect = 'none'
},
handleAIPanelDrag(event) {
if (!this.aiPanelDragging) return
const panel = this.$refs.aiChatPanel
if (!panel) return
const panelWidth = panel.offsetWidth || 380
const panelHeight = panel.offsetHeight || 620
const minLeft = 0
const minTop = 0
const maxLeft = Math.max(window.innerWidth - panelWidth, 0)
const maxTop = Math.max(window.innerHeight - panelHeight, 0)
const nextLeft = event.clientX - this.aiPanelDragOffset.x
const nextTop = event.clientY - this.aiPanelDragOffset.y
this.aiPanelPosition = {
left: Math.min(Math.max(nextLeft, minLeft), maxLeft),
top: Math.min(Math.max(nextTop, minTop), maxTop)
}
},
stopAIPanelDrag() {
this.aiPanelDragging = false
window.removeEventListener('mousemove', this.handleAIPanelDrag)
window.removeEventListener('mouseup', this.stopAIPanelDrag)
document.body.style.userSelect = ''
},
handleAIKeydown(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
@ -1251,6 +1349,7 @@ export default Vue.extend({
justify-content: space-between;
border-bottom: 1px solid #f0f0f0;
background: #fff;
cursor: move;
}
&__title {