修复手机端个人空间选项卡滑动问题

- 为选项卡容器添加 width: 100% 和 max-width: 100% 限制宽度
- 添加 -webkit-overflow-scrolling: touch 启用 iOS 平滑滚动
- 添加 touch-action: pan-x 明确允许横向触摸滚动
- 添加 scroll-behavior: smooth 实现平滑滚动效果
- 添加 overscroll-behavior-x: contain 防止滚动溢出

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
JOJO 2026-03-08 04:52:30 +08:00
parent 823b1e105e
commit 5ce21eb280
4 changed files with 658 additions and 20 deletions

View File

@ -366,6 +366,19 @@
<button type="button" class="mobile-panel-fab" aria-label="切换工作区" @click="toggleMobileOverlayMenu"> <button type="button" class="mobile-panel-fab" aria-label="切换工作区" @click="toggleMobileOverlayMenu">
<img :src="mobilePanelIcon" alt="" aria-hidden="true" /> <img :src="mobilePanelIcon" alt="" aria-hidden="true" />
</button> </button>
<button
type="button"
class="mobile-topbar-selector"
:class="{ open: headerMenuOpen }"
@click.stop="toggleHeaderMenu"
:disabled="!isConnected"
>
<span class="mobile-selector-label">
<span class="mobile-selector-model">{{ currentModelLabel }}</span>
<span class="mobile-selector-sep">·</span>
<span class="mobile-selector-mode">{{ headerRunModeLabel }}</span>
</span>
</button>
<div class="mobile-topbar-title" :title="currentConversationTitle || '未命名对话'"> <div class="mobile-topbar-title" :title="currentConversationTitle || '未命名对话'">
{{ currentConversationTitle || '未命名对话' }} {{ currentConversationTitle || '未命名对话' }}
</div> </div>
@ -394,14 +407,50 @@
<button type="button" class="mobile-menu-btn" aria-label="工作台" @click="openMobileOverlay('workspace')"> <button type="button" class="mobile-menu-btn" aria-label="工作台" @click="openMobileOverlay('workspace')">
<img :src="mobileMenuIcons.workspace" alt="" aria-hidden="true" /> <img :src="mobileMenuIcons.workspace" alt="" aria-hidden="true" />
</button> </button>
<button type="button" class="mobile-menu-btn" aria-label="聚焦面板" @click="openMobileOverlay('focus')">
<img :src="mobileMenuIcons.focus" alt="" aria-hidden="true" />
</button>
<button type="button" class="mobile-menu-btn" aria-label="个人空间" @click="handleMobilePersonalClick"> <button type="button" class="mobile-menu-btn" aria-label="个人空间" @click="handleMobilePersonalClick">
<img :src="mobileMenuIcons.personal" alt="" aria-hidden="true" /> <img :src="mobileMenuIcons.personal" alt="" aria-hidden="true" />
</button> </button>
</div> </div>
</transition> </transition>
<transition name="header-menu">
<div
v-if="headerMenuOpen && isMobileViewport"
class="model-mode-dropdown model-mode-dropdown--mobile"
ref="headerMenu"
>
<div class="dropdown-column">
<div class="dropdown-title">模型</div>
<button
v-for="option in modelOptions"
:key="option.key"
type="button"
class="dropdown-item"
:class="{ active: option.key === currentModelKey, disabled: option.disabled }"
@click.stop="handleHeaderModelSelect(option.key, option.disabled)"
:disabled="streamingMessage || !isConnected || option.disabled"
>
<div class="item-label">{{ option.label }}</div>
<span v-if="option.key === currentModelKey" class="item-check"></span>
</button>
</div>
<div class="dropdown-column">
<div class="dropdown-title">运行模式</div>
<button
v-for="option in headerRunModeOptions"
:key="option.value"
type="button"
class="dropdown-item"
:class="{ active: option.value === resolvedRunMode }"
@click.stop="handleHeaderRunModeSelect(option.value)"
:disabled="streamingMessage || !isConnected"
>
<div class="item-label">{{ option.label }}</div>
<span v-if="option.value === resolvedRunMode" class="item-check"></span>
</button>
</div>
</div>
</transition>
</div> </div>
<transition name="mobile-panel-overlay"> <transition name="mobile-panel-overlay">

View File

@ -762,6 +762,10 @@ const shiftTab = (direction: 1 | -1) => {
}; };
const handleSwipeStart = (event: TouchEvent) => { const handleSwipeStart = (event: TouchEvent) => {
//
if (window.innerWidth <= 768) {
return;
}
if (!event.touches.length) { if (!event.touches.length) {
return; return;
} }
@ -769,6 +773,10 @@ const handleSwipeStart = (event: TouchEvent) => {
}; };
const handleSwipeEnd = (event: TouchEvent) => { const handleSwipeEnd = (event: TouchEvent) => {
//
if (window.innerWidth <= 768) {
return;
}
if (!swipeState.value.active || !event.changedTouches.length) { if (!swipeState.value.active || !event.changedTouches.length) {
return; return;
} }

View File

@ -130,7 +130,13 @@
} }
.chat-container--mobile { .chat-container--mobile {
padding-top: 68px; padding-top: 52px;
}
@media (max-width: 768px) {
.chat-container--mobile {
padding-top: 44px;
}
} }
.conversation-ribbon__selector { .conversation-ribbon__selector {
@ -207,6 +213,26 @@
z-index: 50; z-index: 50;
} }
.model-mode-dropdown--mobile {
position: fixed;
top: 52px;
left: 12px;
right: auto;
min-width: 0;
max-width: min(480px, calc(100vw - 24px));
grid-template-columns: 1fr 1fr;
gap: 8px;
z-index: 1600;
}
.model-mode-dropdown--mobile .dropdown-item {
grid-template-columns: 1fr auto;
}
.model-mode-dropdown--mobile .item-desc {
display: none;
}
.dropdown-column { .dropdown-column {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -357,6 +357,446 @@
} }
} }
/* 手机端完整适配 */
@media (max-width: 768px) {
.personal-page-overlay {
padding: 0;
align-items: stretch;
}
.personal-page-card {
width: 100%;
height: 100%;
max-height: none;
border-radius: 0;
padding: 16px;
display: flex;
flex-direction: column;
}
.personal-page-header {
flex-direction: column;
align-items: stretch;
gap: 12px;
margin-bottom: 16px;
}
.personal-page-header h2 {
font-size: 20px;
}
.personal-page-header p {
font-size: 13px;
}
.personal-page-actions {
flex-direction: row;
width: 100%;
gap: 8px;
}
.personal-page-logout,
.personal-page-close {
flex: 1;
padding: 10px 16px;
font-size: 13px;
text-align: center;
}
.personalization-body {
flex: 1;
min-height: 0;
overflow: hidden;
}
.personal-form {
height: 100%;
display: flex;
flex-direction: column;
}
.personalization-layout {
display: flex;
flex-direction: column;
gap: 12px;
flex: 1;
min-height: 0;
}
.personal-page-tabs {
flex-direction: row;
padding: 8px;
height: auto;
max-height: none;
width: 100%;
max-width: 100%;
overflow-x: auto;
overflow-y: hidden;
flex-wrap: nowrap;
gap: 8px;
border-radius: 12px;
flex-shrink: 0;
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
touch-action: pan-x;
overscroll-behavior-x: contain;
}
.personal-tab-button {
flex: 0 0 auto;
min-width: 100px;
padding: 10px 14px;
text-align: center;
white-space: nowrap;
font-size: 13px;
touch-action: pan-x;
}
.personal-tab-desc {
display: none;
}
.personalization-content-shell {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.personalization-content {
flex: 1;
min-height: 0;
border-radius: 12px;
padding: 0;
/* 允许内部横向滚动 */
touch-action: pan-y pan-x;
}
.personalization-content .personal-page {
padding: 12px;
height: 100%;
overflow-y: auto;
/* 允许内部横向滚动 */
touch-action: pan-y pan-x;
}
.personalization-sections {
display: flex;
flex-direction: column;
gap: 12px;
}
.personal-left-column,
.personal-right-column {
width: 100%;
max-width: none;
}
.personal-section {
padding: 14px;
border-radius: 12px;
}
.personal-field {
font-size: 13px;
}
.personal-field input {
padding: 10px 12px;
font-size: 14px;
}
.personal-toggle {
padding: 14px;
border-radius: 12px;
}
.toggle-title {
font-size: 14px;
}
.toggle-desc {
font-size: 12px;
}
.consideration-list {
max-height: 200px;
}
.consideration-item {
padding: 10px;
font-size: 13px;
}
.tone-preset-row {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.tone-preset-buttons {
width: 100%;
}
.tone-preset-buttons button {
font-size: 12px;
padding: 6px 12px;
}
.behavior-section {
padding: 16px;
border-radius: 12px;
overflow: visible;
}
.behavior-field {
gap: 12px;
overflow: visible;
}
.field-title {
font-size: 15px;
}
.field-desc,
.behavior-desc {
font-size: 13px;
}
.run-mode-options {
display: flex !important;
grid-template-columns: none;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
gap: 10px;
padding-bottom: 8px;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
/* 确保容器可以滚动 */
width: 100%;
max-width: 100%;
/* 关键:允许触摸滚动 */
touch-action: pan-x;
overscroll-behavior-x: contain;
}
.run-mode-options::-webkit-scrollbar {
height: 4px;
}
.run-mode-options::-webkit-scrollbar-track {
background: transparent;
}
.run-mode-options::-webkit-scrollbar-thumb {
background: rgba(121, 109, 94, 0.3);
border-radius: 4px;
}
.run-mode-card {
flex: 0 0 280px;
min-width: 280px;
max-width: 280px;
padding: 12px;
border-radius: 12px;
scroll-snap-align: start;
/* 防止卡片内的触摸事件干扰滚动 */
touch-action: pan-x;
}
.run-mode-title {
font-size: 14px;
}
.run-mode-desc {
font-size: 12px;
}
.run-mode-badge {
font-size: 11px;
padding: 2px 6px;
}
.thinking-presets {
display: flex;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
gap: 8px;
padding-bottom: 8px;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
width: 100%;
max-width: 100%;
touch-action: pan-x;
overscroll-behavior-x: contain;
}
.thinking-presets::-webkit-scrollbar {
height: 4px;
}
.thinking-presets::-webkit-scrollbar-track {
background: transparent;
}
.thinking-presets::-webkit-scrollbar-thumb {
background: rgba(121, 109, 94, 0.3);
border-radius: 4px;
}
.thinking-presets button {
flex: 0 0 auto;
min-width: 100px;
max-width: 150px;
padding: 10px;
scroll-snap-align: start;
touch-action: pan-x;
}
.thinking-input-row {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
.thinking-input-row label {
width: 100%;
}
.thinking-input-row input {
width: 100%;
}
.tool-category-grid {
gap: 8px;
}
.tool-category-chip {
padding: 8px 10px;
font-size: 12px;
}
.personal-form-actions {
flex-direction: column;
gap: 10px;
padding: 12px 0 0;
}
.personal-form-actions .primary {
width: 100%;
padding: 12px 20px;
}
.personal-status-group {
width: 100%;
justify-content: center;
}
.personal-actions-row {
margin-top: 12px;
}
.personal-actions-row .personal-form-actions {
max-width: none;
width: 100%;
}
.theme-grid {
display: flex;
flex-direction: row;
overflow-x: auto;
overflow-y: hidden;
gap: 12px;
padding-bottom: 8px;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
width: 100%;
max-width: 100%;
touch-action: pan-x;
overscroll-behavior-x: contain;
}
.theme-grid::-webkit-scrollbar {
height: 4px;
}
.theme-grid::-webkit-scrollbar-track {
background: transparent;
}
.theme-grid::-webkit-scrollbar-thumb {
background: rgba(121, 109, 94, 0.3);
border-radius: 4px;
}
.theme-card {
flex: 0 0 260px;
min-width: 260px;
max-width: 260px;
padding: 14px;
scroll-snap-align: start;
touch-action: pan-x;
}
.experiment-hero {
grid-template-columns: 1fr;
gap: 16px;
}
.experiment-visual {
min-height: 160px;
border-radius: 20px;
}
.experiment-copy h3 {
font-size: 20px;
}
.experiment-toggle-card {
padding: 16px;
border-radius: 16px;
}
.experiment-toggle-info h4 {
font-size: 16px;
}
.experiment-note {
padding: 14px 16px;
border-radius: 14px;
}
.admin-monitor-panel {
padding: 16px;
border-radius: 16px;
}
.admin-monitor-title-row h3 {
font-size: 18px;
}
.admin-monitor-desc {
font-size: 13px;
}
.admin-monitor-actions {
flex-direction: column;
align-items: stretch;
}
.admin-monitor-button {
width: 100%;
justify-content: center;
padding: 12px 20px;
}
.section-note {
font-size: 11px;
}
}
.behavior-section { .behavior-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -682,6 +1122,13 @@
gap: 12px; gap: 12px;
} }
@media (max-width: 768px) {
.run-mode-options {
display: flex !important;
grid-template-columns: none;
}
}
.run-mode-card { .run-mode-card {
border: 1px solid var(--theme-control-border); border: 1px solid var(--theme-control-border);
border-radius: 16px; border-radius: 16px;
@ -981,21 +1428,51 @@
} }
.mobile-panel-topbar { .mobile-panel-topbar {
background: var(--claude-left-rail); background: var(--chat-surface-color, #ffffff);
padding: 10px 16px; padding: 6px 12px;
display: flex; display: flex;
align-items: center; align-items: flex-start;
gap: 12px; gap: 8px;
box-shadow: 0 10px 20px rgba(51, 35, 20, 0.1); box-shadow: none;
pointer-events: auto; pointer-events: auto;
position: relative;
height: 44px;
}
.mobile-panel-topbar::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: -8px;
height: 16px;
background: var(--chat-surface-color, #ffffff);
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.6) 55%, rgba(0, 0, 0, 0) 100%);
-webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.6) 55%, rgba(0, 0, 0, 0) 100%);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
pointer-events: none;
}
@media (max-width: 768px) {
.mobile-panel-topbar {
height: 36px;
padding: 3px 10px;
gap: 6px;
}
.mobile-panel-topbar::after {
bottom: -8px;
height: 16px;
}
} }
.mobile-panel-fab { .mobile-panel-fab {
width: 44px; width: 32px;
height: 44px; height: 32px;
border: none; border: none;
padding: 0; padding: 0;
border-radius: 12px; border-radius: 8px;
background: transparent; background: transparent;
color: var(--claude-text); color: var(--claude-text);
cursor: pointer; cursor: pointer;
@ -1008,8 +1485,20 @@
} }
.mobile-panel-fab img { .mobile-panel-fab img {
width: 32px; width: 22px;
height: 32px; height: 22px;
}
@media (max-width: 768px) {
.mobile-panel-fab {
width: 30px;
height: 30px;
}
.mobile-panel-fab img {
width: 22px;
height: 22px;
}
} }
.mobile-panel-menu { .mobile-panel-menu {
@ -1026,16 +1515,82 @@
} }
.mobile-topbar-title { .mobile-topbar-title {
flex: 0 0 50%; flex: 1;
max-width: 50%;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 700;
color: var(--claude-text); color: #0f172a;
margin-left: auto; font-family: 'Iowan Old Style', ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
text-align: right;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
text-align: right;
margin-left: auto;
padding-top: 4px;
}
@media (max-width: 768px) {
.mobile-topbar-title {
padding-top: 3px;
}
}
.mobile-topbar-selector {
pointer-events: auto;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 0 0 0;
border-radius: 0;
border: none;
background: transparent;
box-shadow: none;
color: #0f172a;
font-weight: 600;
font-size: 14px;
line-height: 1.35;
font-family: 'Iowan Old Style', ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
cursor: pointer;
transition: color 0.15s ease;
flex-shrink: 0;
}
@media (max-width: 768px) {
.mobile-topbar-selector {
padding: 3px 0 0 0;
}
}
.mobile-topbar-selector:hover {
color: #111827;
}
.mobile-topbar-selector:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.mobile-topbar-selector.open {
color: #111827;
}
.mobile-selector-label {
display: inline-flex;
align-items: center;
gap: 4px;
}
.mobile-selector-model {
font-weight: 700;
}
.mobile-selector-sep {
opacity: 0.6;
}
.mobile-selector-mode {
font-weight: 500;
color: rgba(15, 23, 42, 0.8);
} }
.mobile-menu-btn { .mobile-menu-btn {