From 0d97ae0098d5e09b0309664998459143417ae0cb Mon Sep 17 00:00:00 2001 From: Estrella Pan Date: Thu, 22 Oct 2026 19:34:57 +0200 Subject: [PATCH] =?UTF-8?q?fix(webui):=20=E5=85=A8=E7=95=8C=E9=9D=A2=20UI?= =?UTF-8?q?=20=E5=AE=A1=E6=9F=A5=E4=BF=AE=E5=A4=8D=EF=BC=9A=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E4=B8=8E=E6=96=87=E5=AD=97=E5=AF=B9=E9=BD=90=E3=80=81?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E3=80=81=E5=B8=83=E5=B1=80=E9=94=99=E4=B9=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用 Playwright 在桌面/平板/手机、浅色/深色、中英文下截取全部页面与弹窗, 由干净上下文的审查者逐张检查,共修复 122 个问题(高严重度 12 个)。 - 布局:手机搜索结果行、矮屏登录页与向导、下载器表格响应式、日历长标题撑破网格、 深色模式下 Passkey 列表与种子复选框不可见、设置页新下载器行对齐 - 对齐:向导、登录页、添加 RSS 改用 ab-input / ab-button,控件高度与圆角统一 - 动画:向导步骤、插件页离场、归档区折叠、插件选项展开等过渡不再跳动或闪烁 - 日历:触屏上看不见的取消置顶按钮不再可点 - 私有弹窗改用 ab-modal;更新说明改用 renderMarkdown(链接只接受 http(s)) - 空状态步骤样式合并到 global.scss --- webui/src/components/ab-add-rss.vue | 111 +--- webui/src/components/ab-bangumi-card.vue | 45 +- webui/src/components/ab-container.vue | 11 + webui/src/components/ab-edit-rule.vue | 196 +++--- webui/src/components/ab-setting.vue | 13 +- webui/src/components/ab-status-bar.vue | 12 +- webui/src/components/ab-torrent-list-page.vue | 14 +- webui/src/components/advanced-section.vue | 56 +- webui/src/components/bangumi-filter-field.vue | 66 +- webui/src/components/bangumi-info-tags.vue | 46 +- webui/src/components/bangumi-offset-field.vue | 54 +- webui/src/components/bangumi-preview.vue | 12 +- webui/src/components/bangumi-rss-link-row.vue | 37 +- webui/src/components/basic/ab-button.vue | 33 +- webui/src/components/basic/ab-input.vue | 8 +- webui/src/components/basic/ab-list.vue | 7 +- .../basic/ab-offset-mismatch-dialog.vue | 437 +++++-------- webui/src/components/basic/ab-progress.vue | 7 +- webui/src/components/basic/ab-select.vue | 7 + webui/src/components/basic/ab-tags-input.vue | 3 +- webui/src/components/basic/ab-tooltip.vue | 11 +- .../components/calendar/calendar-board.vue | 33 +- .../src/components/calendar/calendar-card.vue | 124 ++-- .../calendar/calendar-day-column.vue | 74 +-- .../calendar/calendar-mobile-list.vue | 108 +--- .../calendar/calendar-rule-list-popup.vue | 30 +- .../layout/ab-notification-center.vue | 84 ++- webui/src/components/layout/ab-sidebar.vue | 59 +- .../src/components/market/market-confirm.vue | 14 + webui/src/components/plugin-slot.vue | 12 +- .../components/search/ab-search-confirm.vue | 576 ++++++------------ .../src/components/search/ab-search-modal.vue | 362 +++++------ .../__tests__/plugin-schema-form.test.ts | 5 +- .../src/components/setting/config-access.vue | 46 +- .../components/setting/config-download.vue | 33 +- webui/src/components/setting/config-llm.vue | 323 +++++----- .../setting/config-notification.vue | 90 +-- .../src/components/setting/config-passkey.vue | 41 +- .../src/components/setting/config-plugins.vue | 22 + .../setting/config-search-provider.vue | 105 ++-- .../components/setting/llm-auth-dialog.vue | 81 ++- .../components/setting/plugin-schema-form.vue | 25 +- webui/src/components/setting/update-card.vue | 98 +-- .../src/components/setup/wizard-container.vue | 8 +- .../components/setup/wizard-step-account.vue | 43 +- .../setup/wizard-step-downloader.vue | 63 +- .../setup/wizard-step-notification.vue | 52 +- .../components/setup/wizard-step-review.vue | 14 +- .../src/components/setup/wizard-step-rss.vue | 45 +- webui/src/i18n/en.json | 14 +- webui/src/i18n/zh-CN.json | 24 +- webui/src/pages/index.vue | 21 +- .../index/bangumi-torrents/[id]/index.vue | 14 +- webui/src/pages/index/bangumi.vue | 449 +++++--------- webui/src/pages/index/calendar.vue | 110 +--- webui/src/pages/index/config.vue | 28 +- webui/src/pages/index/downloader.vue | 521 +++++++--------- webui/src/pages/index/log.vue | 106 ++-- webui/src/pages/index/market/[[id]].vue | 203 ++++-- webui/src/pages/index/player.vue | 203 +----- webui/src/pages/index/plugins/[id].vue | 28 +- .../pages/index/plugins/plugin-page.test.ts | 3 + webui/src/pages/index/rss.vue | 193 ++++-- webui/src/pages/login.vue | 70 +-- webui/src/pages/setup.vue | 27 +- webui/src/style/global.scss | 26 + webui/src/style/var.scss | 3 +- webui/src/utils/markdown.ts | 23 +- 68 files changed, 2464 insertions(+), 3358 deletions(-) diff --git a/webui/src/components/ab-add-rss.vue b/webui/src/components/ab-add-rss.vue index d998b267..5e358fe3 100644 --- a/webui/src/components/ab-add-rss.vue +++ b/webui/src/components/ab-add-rss.vue @@ -1,6 +1,5 @@ diff --git a/webui/src/components/bangumi-info-tags.vue b/webui/src/components/bangumi-info-tags.vue index 75ce63be..a4f5b519 100644 --- a/webui/src/components/bangumi-info-tags.vue +++ b/webui/src/components/bangumi-info-tags.vue @@ -8,14 +8,14 @@ defineProps<{ @@ -23,42 +23,6 @@ defineProps<{ .info-tags { display: flex; flex-wrap: wrap; - gap: 8px; - margin-bottom: 16px; -} - -.info-tag { - display: inline-flex; - align-items: center; - padding: 6px 14px; - border-radius: var(--radius-full); - font-size: 13px; - font-weight: 600; - - &--season { - background: color-mix(in srgb, var(--color-primary) 12%, transparent); - color: var(--color-primary); - } - - &--resolution { - background: color-mix(in srgb, var(--color-accent) 12%, transparent); - color: var(--color-accent); - } - - &--subtitle { - background: color-mix(in srgb, var(--color-success) 12%, transparent); - color: var(--color-success); - } - - &--group { - background: color-mix(in srgb, var(--color-warning) 12%, transparent); - color: var(--color-warning); - } - - // 剧场版/特别篇:反白强调,与常规元数据标签区分 - &--etype { - background: var(--color-primary); - color: var(--color-white); - } + gap: 6px; } diff --git a/webui/src/components/bangumi-offset-field.vue b/webui/src/components/bangumi-offset-field.vue index fb6e4b91..4079dbff 100644 --- a/webui/src/components/bangumi-offset-field.vue +++ b/webui/src/components/bangumi-offset-field.vue @@ -1,4 +1,6 @@ diff --git a/webui/src/components/basic/ab-button.vue b/webui/src/components/basic/ab-button.vue index 628f97d0..f17d523b 100644 --- a/webui/src/components/basic/ab-button.vue +++ b/webui/src/components/basic/ab-button.vue @@ -28,20 +28,29 @@ function onClick(event: MouseEvent) { diff --git a/webui/src/components/basic/ab-progress.vue b/webui/src/components/basic/ab-progress.vue index f24b6f23..98d65602 100644 --- a/webui/src/components/basic/ab-progress.vue +++ b/webui/src/components/basic/ab-progress.vue @@ -1,5 +1,5 @@ + +
+ + + +
@@ -503,6 +547,10 @@ function permissionLabel(id: string) { } .market__tabs { + grid-area: tabs; + // 与原先在 header 外时一样,离搜索行 12px(header 行距 8px) + margin-top: 4px; + @include forMobile { max-width: 100%; overflow-x: auto; @@ -511,7 +559,7 @@ function permissionLabel(id: string) { // 选中下划线(margin-bottom:-1px),焦点框也被裁。上下留 4px 并用背景线画边框, // 负 margin 抵消留白,布局与边线位置不变。 padding-block: 4px; - margin-block: -4px -3px; + margin-block: 0 -3px; border-bottom: none; background: linear-gradient(var(--color-border), var(--color-border)) left bottom 3px / 100% 1px no-repeat; @@ -527,22 +575,42 @@ function permissionLabel(id: string) { } } +// 手机:标题 | 目录状态 / 搜索 | 状态 / 分类; +// 平板以上:搜索 | 状态 / 分类 | 目录状态 —— 目录状态宽度变化不再推动搜索框与下拉 .market__top { display: grid; grid-template-areas: 'title meta' - 'search state'; + 'search state' + 'tabs tabs'; grid-template-columns: minmax(0, 1fr) 128px; align-items: center; gap: 8px; @include forTablet { - grid-template-areas: 'search state meta'; - grid-template-columns: minmax(0, 1fr) 140px auto; + grid-template-areas: + 'search search state' + 'tabs meta meta'; + grid-template-columns: minmax(0, 1fr) auto 140px; gap: 8px 12px; } } +// 与 ab-segmented 同高:标签行 27px + 底线(手机/平板由 .market__tabs 加上下 4px 留白) +.market__tabs--placeholder { + box-sizing: content-box; + height: 27px; + + // 桌面画 ab-segmented 的底线;手机/平板已由 .market__tabs 的背景线画出 + @include forDesktop { + border-bottom: 1px solid var(--color-border); + } + + @include forTouch { + height: 35px; + } +} + .market__title, .market__bar-title { margin: 0; @@ -584,7 +652,6 @@ function permissionLabel(id: string) { } .market__time { - font-family: var(--font-mono); font-variant-numeric: tabular-nums; } @@ -615,10 +682,13 @@ function permissionLabel(id: string) { min-width: 0; } +// 长名称最多两行,完整名称在 title 提示里 .market__bar-title { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow-wrap: anywhere; } // 桌面:列表 + 详情双栏,各自滚动;手机:只显示其中一栏 @@ -647,11 +717,17 @@ function permissionLabel(id: string) { @include forTablet { border-right: 1px solid var(--color-border); } + + // 手机上从详情返回时列表按 v-if 重新挂载:与进入详情同样的淡入上移 + @include forMobile { + animation: detail-enter var(--transition-normal) both; + } } +// 右列最多占 45%,名称至少保留一半以上 .row { display: grid; - grid-template-columns: 1fr auto; + grid-template-columns: minmax(0, 1fr) fit-content(45%); align-items: baseline; gap: 2px 10px; flex-shrink: 0; @@ -696,9 +772,13 @@ function permissionLabel(id: string) { } .row__version { + min-width: 0; + overflow: hidden; color: var(--color-text-secondary); font-size: 12px; text-align: right; + text-overflow: ellipsis; + white-space: nowrap; } .row__state { @@ -747,6 +827,8 @@ function permissionLabel(id: string) { min-width: 0; min-height: 0; overflow: auto; + // 换插件 / 进入确认时按 key 重建:与路由切换同样的淡入上移 + animation: detail-enter var(--transition-normal) both; @include forTablet { padding: 16px 20px 0; @@ -768,21 +850,28 @@ function permissionLabel(id: string) { margin: 0; font-size: 17px; font-weight: 650; + overflow-wrap: anywhere; } .detail__desc { margin: 2px 0 0; color: var(--color-text-secondary); font-size: 13px; + overflow-wrap: anywhere; } .detail__kv { display: grid; grid-template-columns: minmax(64px, max-content) 1fr; + align-items: baseline; gap: 8px 14px; margin: 0; font-size: 13px; + @include forMobile { + grid-template-columns: minmax(64px, 7.5em) 1fr; + } + dt { color: var(--color-text-secondary); } @@ -888,5 +977,33 @@ function permissionLabel(id: string) { :deep(a) { color: var(--color-primary); } + + :deep(table) { + display: block; + overflow-x: auto; + border-collapse: collapse; + margin: 0 0 8px; + font-size: 12px; + overflow-wrap: normal; + } + + :deep(th), + :deep(td) { + padding: 4px 8px; + border-bottom: 1px solid var(--color-border); + text-align: left; + vertical-align: top; + } + + :deep(th) { + font-weight: 600; + } +} + +@keyframes detail-enter { + from { + opacity: 0; + transform: translateY(6px); + } } diff --git a/webui/src/pages/index/player.vue b/webui/src/pages/index/player.vue index f9195d2f..aed43dda 100644 --- a/webui/src/pages/index/player.vue +++ b/webui/src/pages/index/player.vue @@ -8,67 +8,25 @@ const { url } = storeToRefs(usePlayerStore());