-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat(dashboard): 优化前端在手机端的表现 #8447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,55 +81,57 @@ | |
| /> | ||
| </div> | ||
|
|
||
| <div v-if="!isSidebarCollapsed" class="session-list"> | ||
| <div | ||
| v-for="session in sessions" | ||
| :key="session.session_id" | ||
| class="session-item" | ||
| :class="{ active: !isProviderWorkspace && currSessionId === session.session_id }" | ||
| role="button" | ||
| tabindex="0" | ||
| @click="selectSession(session.session_id)" | ||
| @keydown.enter="selectSession(session.session_id)" | ||
| @keydown.space.prevent="selectSession(session.session_id)" | ||
| > | ||
| <span v-if="!isSidebarCollapsed" class="session-title">{{ | ||
| sessionTitle(session) | ||
| }}</span> | ||
| <div class="session-actions" @click.stop> | ||
| <v-btn | ||
| icon="mdi-pencil-outline" | ||
| size="x-small" | ||
| variant="text" | ||
| class="session-action-btn" | ||
| :title="tm('conversation.editDisplayName')" | ||
| @click="editSidebarSessionTitle(session)" | ||
| /> | ||
| <v-btn | ||
| icon="mdi-delete-outline" | ||
| size="x-small" | ||
| variant="text" | ||
| class="session-action-btn" | ||
| :title="tm('actions.deleteChat')" | ||
| @click="deleteSidebarSession(session)" | ||
| <OverlayScrollbar v-if="!isSidebarCollapsed" class="session-list-scroll"> | ||
| <div class="session-list"> | ||
| <div | ||
| v-for="session in sessions" | ||
| :key="session.session_id" | ||
| class="session-item" | ||
| :class="{ active: !isProviderWorkspace && currSessionId === session.session_id }" | ||
| role="button" | ||
| tabindex="0" | ||
| @click="selectSession(session.session_id)" | ||
| @keydown.enter="selectSession(session.session_id)" | ||
| @keydown.space.prevent="selectSession(session.session_id)" | ||
| > | ||
| <span v-if="!isSidebarCollapsed" class="session-title">{{ | ||
| sessionTitle(session) | ||
| }}</span> | ||
| <div class="session-actions" @click.stop> | ||
| <v-btn | ||
| icon="mdi-pencil-outline" | ||
| size="x-small" | ||
| variant="text" | ||
| class="session-action-btn" | ||
| :title="tm('conversation.editDisplayName')" | ||
| @click="editSidebarSessionTitle(session)" | ||
| /> | ||
| <v-btn | ||
| icon="mdi-delete-outline" | ||
| size="x-small" | ||
| variant="text" | ||
| class="session-action-btn" | ||
| :title="tm('actions.deleteChat')" | ||
| @click="deleteSidebarSession(session)" | ||
| /> | ||
| </div> | ||
| <v-progress-circular | ||
| v-if="isSessionRunning(session.session_id)" | ||
| class="session-progress" | ||
| indeterminate | ||
| size="16" | ||
| width="2" | ||
| /> | ||
| </div> | ||
| <v-progress-circular | ||
| v-if="isSessionRunning(session.session_id)" | ||
| class="session-progress" | ||
| indeterminate | ||
| size="16" | ||
| width="2" | ||
| /> | ||
| </div> | ||
|
|
||
| <div | ||
| v-if="!isSidebarCollapsed && !sessions.length && !loadingSessions" | ||
| class="empty-sessions" | ||
| > | ||
| {{ tm("conversation.noHistory") }} | ||
| <div | ||
| v-if="!isSidebarCollapsed && !sessions.length && !loadingSessions" | ||
| class="empty-sessions" | ||
| > | ||
| {{ tm("conversation.noHistory") }} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </OverlayScrollbar> | ||
|
|
||
| <div class="sidebar-footer"> | ||
| <StyledMenu | ||
|
|
@@ -337,14 +339,16 @@ | |
| </ProjectView> | ||
|
|
||
| <template v-else> | ||
| <section | ||
| ref="messagesContainer" | ||
| <OverlayScrollbar | ||
| ref="messagesScrollbar" | ||
| class="messages-panel" | ||
| @scroll="handleMessagesScroll" | ||
| > | ||
| <div v-if="loadingMessages" class="center-state"> | ||
| <v-progress-circular indeterminate size="32" width="3" /> | ||
| </div> | ||
| <section | ||
| class="messages-panel-inner" | ||
| > | ||
| <div v-if="loadingMessages" class="center-state"> | ||
| <v-progress-circular indeterminate size="32" width="3" /> | ||
| </div> | ||
|
|
||
| <div v-else-if="sessionProject" class="session-project-breadcrumb"> | ||
| <span>{{ sessionProject.title }}</span> | ||
|
|
@@ -387,6 +391,7 @@ | |
| /> | ||
| </div> | ||
| </section> | ||
| </OverlayScrollbar> | ||
|
|
||
| <section class="composer-shell"> | ||
| <ChatInput | ||
|
|
@@ -505,6 +510,7 @@ import { useRoute, useRouter } from "vue-router"; | |
| import { useDisplay } from "vuetify"; | ||
| import axios from "axios"; | ||
| import StyledMenu from "@/components/shared/StyledMenu.vue"; | ||
| import OverlayScrollbar from "@/components/shared/OverlayScrollbar.vue"; | ||
| import ProjectDialog, { | ||
| type ProjectFormData, | ||
| } from "@/components/chat/ProjectDialog.vue"; | ||
|
|
@@ -607,6 +613,7 @@ const projectSessions = ref<Session[]>([]); | |
| const loadingSessions = ref(false); | ||
| const draft = ref(""); | ||
| const messagesContainer = ref<HTMLElement | null>(null); | ||
| const messagesScrollbar = ref<InstanceType<typeof OverlayScrollbar> | null>(null); | ||
| const inputRef = ref<InstanceType<typeof ChatInput> | null>(null); | ||
| const shouldStickToBottom = ref(true); | ||
| const replyTarget = ref<ChatRecord | null>(null); | ||
|
|
@@ -766,6 +773,13 @@ onMounted(async () => { | |
| } finally { | ||
| loadingSessions.value = false; | ||
| } | ||
| // Bind messagesContainer to OverlayScrollbar viewport | ||
| nextTick(() => { | ||
| if (messagesScrollbar.value?.viewport) { | ||
| messagesContainer.value = messagesScrollbar.value.viewport; | ||
| messagesContainer.value.addEventListener('scroll', handleMessagesScroll); | ||
| } | ||
| }); | ||
| }); | ||
|
Comment on lines
+776
to
783
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 在 由于 解决方案: |
||
|
|
||
| onBeforeUnmount(() => { | ||
|
|
@@ -1496,9 +1510,12 @@ function toggleTheme() { | |
| transform: rotate(180deg); | ||
| } | ||
|
|
||
| .session-list { | ||
| .session-list-scroll { | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| min-height: 0; | ||
| } | ||
|
|
||
| .session-list { | ||
| padding: 4px 12px 12px; | ||
| display: flex; | ||
| flex-direction: column; | ||
|
|
@@ -1640,7 +1657,9 @@ function toggleTheme() { | |
| .messages-panel { | ||
| flex: 1; | ||
| min-height: 0; | ||
| overflow-y: auto; | ||
| } | ||
|
|
||
| .messages-panel-inner { | ||
| padding: 24px max(24px, calc((100% - 980px) / 2)) 18px; | ||
| } | ||
|
|
||
|
|
@@ -1773,13 +1792,8 @@ kbd { | |
| } | ||
|
|
||
| @media (max-width: 760px) { | ||
| .messages-panel { | ||
| .messages-panel-inner { | ||
| padding: 18px 14px; | ||
| } | ||
|
|
||
| .composer-shell, | ||
| .project-composer-shell { | ||
| padding: 0; | ||
| } | ||
| } | ||
| </style> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): The scroll listener attached in
nextTickis never explicitly removed, which can lead to subtle leaks across remounts.Because the handler is manually attached to
messagesContainerinnextTick, there should be a matchingremoveEventListenerinonBeforeUnmount(or use a composable that handles setup/teardown). This prevents leaks or unexpected behavior ifmessagesContaineris reused or the component is mounted/unmounted often.Suggested implementation:
The snippet you provided shows
transform: rotate(180deg);insideonBeforeUnmount, which looks like a truncated or mis-placed style declaration. If this is not actually part of the unmount hook in your real file, you should instead:onBeforeUnmounthook (or create one if it doesn’t exist).onBeforeUnmount(if present) is preserved alongside this cleanup.