AutoBangumi 搜索面板重设计:从下拉列表到可过滤的模态搜索体验

发布时间:2026/9/27 10:11:23
AutoBangumi 搜索面板重设计:从下拉列表到可过滤的模态搜索体验 后端前端音视频【免费下载链接】Auto_BangumiAutoBangumi - 全自动追番工具项目地址https://gitcode.com/gh_mirrors/au/Auto_Bangumi点击查看免费下载本文基于 AutoBangumi 仓库内的设计文档 docs/plans/2026-01-25-search-panel-redesign.md结合前端 WebUI 与后端搜索链路的实际源码完整梳理搜索面板的交互重构方案与落地实现。读者可以从中掌握原搜索栏的痛点、模态搜索面板的结构与行为规范、四维过滤芯片系统的数据流、订阅确认模态的设计以及 SSE 流式搜索在前端如何与后端解析器协作。AutoBangumiAB的种子搜索功能自 v3.1 起内置在顶栏中详见 docs/feature/search.md用于快速查找番剧。2026 年初的设计文档将搜索组件从下拉列表重构为全模态 高级过滤的搜索体验。本文将这份设计文档作为主线逐节拆解设计意图并用 webui/src/components/search/ 下的四个组件、webui/src/store/search.ts 的状态管理以及后端 backend/src/module/api/search.py 的 SSE 数据源来佐证落地细节。原实现的四大痛点设计文档首先列出现有下拉式搜索栏的四个问题它们共同构成了重构的直接动机点击外部即清空一切v-on-click-outsideclearSearch会在用户误点空白处时清空关键词与全部结果造成结果意外丢失结果展示能力有限绝对定位absolute的下拉列表没有滚动容器结果一多就无法浏览缺少显式关闭控制用户除了点击外部之外没有其他有意的关闭方式完全没有过滤能力同一番剧的不同字幕组、不同季度的结果混在一起难以快速定位目标版本。对应地设计目标被明确为四条防止搜索结果被意外关闭、支持大量结果的可滚动展示、支持按字幕组 / 分辨率 / 字幕类型 / 季度过滤、以及订阅前提供确认步骤。触发与切换行为设计文档规定了一套明确的开关语义实际在 webui/src/components/ab-search-bar.vue 与 webui/src/components/search/ab-search-modal.vue 中实现点击顶栏搜索框模态未开则打开已开则关闭toggleModal()按Escape关闭模态模态头部有可见的×关闭按钮ab-icon-buttonClose图标handleClose()触发点击背板backdrop不会关闭模板中背板元素modal-backdrop本身不挂点击事件只有模态容器自身的click.selfhandleClose会响应——点击遮罩空白处不会清空结果这正对应设计文档防止意外丢失结果的目标。一个值得注意的实现细节是Escape监听器onKeyStroke(Escape, ...)ab-search-modal.vue是常驻挂载的组件因此监听器内部必须先判断showModal否则在整个应用的任何页面按Escape都会触发模态切换在确认模态打开时第一次Escape只清空selectedResult回到搜索结果第二次才关闭搜索模态。模态布局结构设计文档给出了完整布局示意核心结构为固定头部搜索输入 资源站选择器 关闭按钮、粘性过滤芯片区、可滚动结果网格┌─────────────────────────────────────────────────────────┐ │ [Search input] [provider ▼] [×] │ ├─────────────────────────────────────────────────────────┤ │ 字幕组: [喵萌奶茶屋] [ANi] [桜都] [LoliHouse] [3] │ │ 分辨率: [1080p] [720p] [4K] │ │ 字幕语言: [简中 CHS] [繁中 CHT] [双语] [内嵌] [外挂] │ │ 季度: [S1] [S2] [剧场版] │ │ [清除筛选] 8/24 结果 │ ├─────────────────────────────────────────────────────────┤ │ (scrollable grid of result cards, 3 columns on desktop) │ └─────────────────────────────────────────────────────────┘实际实现ab-search-modal.vue模板与文档略有演进模态通过Teleport tobody挂载到bodymodal-container使用position: fixed 居中弹性布局modal-content最大宽度 1100px、最大高度calc(100dvh - 100px)并启用内部滚动。头部左侧是带放大镜按钮搜索中替换为NSpin的输入框中间是资源站下拉provider-selectaria-haspopuplistbox右侧是关闭按钮。资源站列表来自useSearchStore().getProviders()即后端/api/v1/search/provider返回的可用站点默认mikan、anibt、dmhy、nyaa。过滤芯片系统四个过滤维度维度说明典型取值字幕组字幕组名称喵萌奶茶屋、ANi、桜都、LoliHouse分辨率视频分辨率720p、1080p、4K/2160p字幕语言字幕类型CHS简中、CHT繁中、双语、内嵌、外挂 ASS/SRT季度季度/类型S1、S2、剧场版/Movie、OVA自动生成的过滤选项设计文档要求结果流式到达时逐维度提取唯一值芯片随新值动态出现。这在 webui/src/store/search.ts 的groupedResults与模态内的filterOptionscomputed 中实现groupedResults以official_title || title_raw为 key 将流式结果分组成GroupedBangumi { key, official_title, poster_link, year, variants }随后filterOptions遍历所有 group 的 variants用Set收集四个维度的去重取值。为让零散的原始元数据收敛成可过滤的规范取值源码提供三个归一化函数normalizeResolution(raw)4k/2160/uhd → 4K1080/fhd/1920 → FHD720/hd → HD480/sd → SD未命中时原样返回normalizeSubtitle(raw)先判断双语含双语/dual/简繁/CHSCHT再分别归一化简中简/chs/sc、繁中繁/cht/tc、日文日/jp/ja、内嵌内嵌/内封、外挂外挂/ass/srt并且简 内嵌会组合成简/内嵌这种复合值normalizeSeason(raw)S\d原样大写纯数字提取为S{n}剧场/movie/劇場 → 剧场版ova → OVAsp/special → SP。各维度的排序规则也在 computed 中定义分辨率按4K → FHD → HD → SD的固定序未知项排末尾字幕按简 → 繁 → 双语 → 简/内嵌 → 繁/内嵌 → 内嵌 → 外挂 → 日季度按S1、S2、S3…数字升序后再排特殊类型。这些规范化的取值随后渲染为芯片标题旁的类别图标来自icon-park/vue-next字幕组PeoplesTwo、分辨率Monitor、字幕Translate、季度Calendar。过滤行为与组合逻辑芯片可切换toggleFilter(type, value)在对应维度数组中增删取值多选可同时生效同类别内 AND、跨类别 ORvariantMatchesFilters()逐一检查四个维度任一维度有激活取值时变体必须命中该维度集合且四个维度条件同时成立才算匹配激活芯片实心高亮.active填充主色未激活为描边样式存在激活筛选时显示清除筛选clearFilters()清空全部四个数组结果计数实时更新筛选中 8 / 24 个结果由filteredVariantCount / totalVariantCount两个 computed 提供。实际实现还额外引入了两个设计文档未明确、但体验价值很高的能力无效选项禁用wouldProduceResults(type, value)会结合当前其他维度的激活值预判如果再点这个芯片是否还能得到结果不能产生结果时芯片置灰disabled避免用户走进零结果死胡同已选芯片摘要selectedFilterTags把四个维度的激活值汇总成可逐个删除的小标签带×与清除筛选按钮并存方便精确回退单个筛选条件。溢出处理设计文档规定某类选项超过 5 个时显示前 4 个 [N more]展开芯片每行可独立折叠。实现中常量MAX_VISIBLE_CHIPS 6getVisibleOptions()在未展开时截取前 6 个hasOverflow()/getOverflowCount()计算剩余数量点击展开按钮在expandedCategoriesSetgroup | resolution | subtitle | season中增删对应类别已展开时按钮文案切换为收起。每个类别的展开状态彼此独立。结果卡片与展示形态设计文档给出的卡片原型是紧凑网格项海报、中文 罗马音双标题、字幕组徽标、分辨率与字幕标签、季度 集数┌──────────────────────────┐ │ ┌──────┐ 葬送的芙莉莲 │ │ │poster│ Frieren │ │ │ │ ───────────── │ │ └──────┘ 喵萌奶茶屋 │ │ 1080p · 简中 │ │ S1 · 全28集 │ └──────────────────────────┘实际落地的展示形态采用了设计稿演化后的海报 变体芯片行布局源码注释标记为 Original Prototype 4每个番剧分组一行左侧是海报无图时显示占位标题块右侧是该分组的所有变体按钮。每个变体芯片展示tag-group字幕组未知时显示Unknown、tag-res归一化分辨率、tag-sub归一化字幕、tag-season归一化季度四枚标签。点击某个变体芯片即选中该版本进入确认模态。为保证大量变体不至于撑爆界面单组变体同样有上限MAX_VISIBLE_VARIANTS 12超出部分通过N展开按钮在expandedVariants中按组展开/收起。而 webui/src/components/search/ab-search-card.vue 中的search-card海报 变体数角标variant-badge 标题则是这一行布局之外保留的卡片语义可在分组视角下继续演进。流式动画与空态/加载态设计文档要求结果卡片淡入上滑opacity: 0 → 1、translateY: 8px → 0相邻卡片延迟 50ms并给出基于 VueTransitionGroup的 CSS 示例.card-enter-active { transition: all 0.3s ease; transition-delay: calc(var(--index) * 50ms); } .card-enter-from { opacity: 0; transform: translateY(8px); }模态本身使用overlay/modal两套过渡实现背板与面板的淡入淡出确认模态则用modal-in关键帧scale(0.95) translateY(10px) → scale(1)入场。四种空态在模板中按优先级依次判定文案来自 webui/src/i18n/zh-CN.json 的search命名空间状态判定条件显示文案初始无输入且无结果输入关键词开始搜索搜索中SSE 连接建立中输入框内 Spinner结果逐个流入无结果有输入但结果为空未找到相关结果试试其他关键词搜索失败searchFailed为真搜索失败请检查连接后重试。筛选后为零筛选不匹配由无效选项禁用 结果计数联动处理订阅确认模态点击结果变体后webui/src/components/search/ab-search-confirm.vue 弹出一层嵌套确认模态z-index: calc(var(--z-modal) 10)设计布局如下┌─────────────────────────────────────────────────────┐ │ 添加订阅 [×] │ ├─────────────────────────────────────────────────────┤ │ ┌────────┐ 葬送的芙莉莲 │ │ │ poster │ Sousou no Frieren │ │ │ │ ★ 9.2 · 2023年秋 · 全28集 │ │ └────────┘ │ ├─────────────────────────────────────────────────────┤ │ RSS 源: [当前选择的RSS链接] [复制] │ │ 字幕组: 喵萌奶茶屋 │ │ 分辨率: 1080p │ │ 字幕类型: 简体中文 (内嵌) │ ├─────────────────────────────────────────────────────┤ │ 高级设置 ▼ │ │ 过滤规则 / 保存路径 / 重命名 │ ├─────────────────────────────────────────────────────┤ │ [取消] [确认订阅 ✓] │ └─────────────────────────────────────────────────────┘实际实现包含以下值得展开的行为本地深拷贝localBangumi JSON.parse(JSON.stringify(props.bangumi))后续编辑不污染 store 中的原始对象watch(() props.bangumi, ..., { deep: true })在切换选中项时重新同步并触发偏移检测元信息标签infoTags按季度/分辨率/字幕/字幕组生成四色标签季节用主色、分辨率用强调色、字幕用成功色、字幕组用警告色RSS 链接一键复制navigator.clipboard.writeText(rssLink)成功后按钮短暂变为CheckOne勾选态2 秒后复原高级设置默认折叠包含三块——过滤规则用NDynamicTags编辑filter数组对应后端 webui/types/bangumi.ts 中BangumiRule.filter: string[]、季度偏移season_offset与集数偏移episode_offset可调用apiBangumi.suggestOffset自动检测偏移不匹配检查挂载时调用apiBangumi.detectOffset({ title, parsed_season, parsed_episode: 1 })若has_mismatch为真弹出ab-offset-mismatch-dialog展示 TMDB 对照信息与建议值用户可选择应用写入season_offset/episode_offset、保留或取消确认与取消语义取消仅关闭确认模态clearSelectedResult搜索结果原样保留确认订阅调用handleConfirm→apiDownload.subscribe(bangumi, rss)成功后提示订阅成功并刷新番剧列表、关闭两层模态。handleConfirm中的订阅对象构造也值得注意它把选中的番剧数据打包为{ id: 0, name: official_title, url: rss_link[0], aggregate: false, parser: provider, enabled: true, ... }的 RSS 记录即搜索订阅本质上等同于添加一条指向该资源站搜索结果的关键词 RSS。键盘导航按键行为Enter搜索框内触发搜索keyup.enteronSearchEscape先关闭确认模态回到结果再关闭搜索模态Tab依次经过过滤芯片 → 结果卡片Enter聚焦卡片上打开确认模态方向键网格导航可选增强onSearch()在 webui/src/store/search.ts 中校验关键词非空后调用openSearch()开启 SSE 流模态内搜索中按钮变为 Spinner 且禁用避免重复提交。响应式设计视口网格列数模态宽度行为桌面1024px3 列800px 居中完整体验平板768–1024px2 列90% 宽过滤收起为单行移动端768px1 列全屏Bottom Sheet 样式实际样式中modal-container的padding: 60px 16px 16px在forDesktop媒体查询下提升为80px 24px 24px模态max-width: 1100px比文档的 800px 更宽以容纳行式变体布局。移动端语义对应全屏 bottom sheet模态撑满inset: 0内容区自身可滚动。组件结构与状态管理设计文档规划的组件树为ab-search-modal.vue (new - main modal container) ├── ab-search-header.vue (search input provider close) ├── ab-search-filters.vue (new - filter chips) ├── ab-search-results.vue (new - scrollable grid) │ └── ab-search-card.vue (new - individual result card) └── ab-search-confirm.vue (new - confirmation modal)实际仓库中的 webui/src/components/search/ 收敛为四个文件ab-search-modal.vue主容器内部内联实现 header、filter 区与结果列表、ab-search-filters.vue独立过滤组件props 为filters/filterOptions/filteredCount/totalCountemittoggle-filter与clear-filters、ab-search-card.vue卡片、ab-search-confirm.vue确认模态。其中ab-search-filters.vue与模态内嵌实现是两套平行实现常量上限分别为 8 与 6说明过滤逻辑正处于组件化抽取的过渡阶段。useSearchStore的状态设计比文档规划更内聚——showModal、selectedResult、groupedResults、providers、loading、searchFailed均收敛在 webui/src/store/search.ts 中过滤状态activeFilters、expandedCategories、expandedVariants则由ab-search-modal.vue以组件内 ref 持有。toggleModal/openModal/closeModal/clearSearch/selectResult/clearSelectedResult构成完整的状态流转 APIcloseModal会同时置空selectedResult并调用closeSearch()关闭 SSE 流。实现要点过滤解析、SSE 流式与后端数据链路设计文档的三条实现要点在源码中均有对应过滤解析元数据来自后端解析器对种子标题的解析结果即BangumiRule的group_name、dpi、subtitle、season_raw/season字段类型定义见 webui/types/bangumi.ts对应后端 backend/src/module/models/bangumi.py 的Bangumi模型前端再经三个normalize*函数规整为过滤取值SSE 流式增量更新前端 webui/src/api/search.ts 用原生EventSource打开api/v1/search/bangumi?site{provider}keywords{keyword}withCredentials: true每条onmessage解析BangumiAPI并拆分为filter: string[]与rss_link: string[]追加前先按rss_link[0] || title_raw去重。源码注释与 webui/src/api/tests/search.test.ts 中的FakeEventSource测试共同记录了关键生命周期治理重搜索前必须关闭旧流且旧流的onopen/onmessage/onerror均不得触碰共享状态通过eventSource.value ! es同流判定否则旧流会重复追加结果、甚至在新流建立后触发无限自动重连新结果立即应用激活筛选过滤逻辑基于groupedResults的 computed 派生流式追加数据后所有过滤与计数自动重算。后端侧backend/src/module/api/search.py 的GET /search/bangumi将关键词按空格拆分后交给SearchTorrent().analyse_keyword()以sse_starlette的EventSourceResponse流式返回backend/src/module/searcher/searcher.py 的analyse_keyword()逐条调用解析器torrent_to_data(..., fetch_posterFalse)交互搜索不做逐条 Mikan 主页抓取与海报下载以保持响应速度按special_url去重后再通过 TMDB 缓存补齐本地化标题与海报最后以json.dumps(bangumi.dict())逐条yield。/search/provider端点则返回SEARCH_CONFIG的全部站点 key前端据此渲染资源站下拉。搜索源的管理URL 模板必须包含%s占位符、默认源 mikan/nyaa/dmhy 不可删除参见 docs/feature/search.md。小结从设计文档到落地代码AutoBangumi 的搜索面板完成了一次完整的交互升级以模态取代下拉、以显式关闭取代点击外部清空、以四维过滤芯片解决同番多版本难以定位的问题、以订阅确认模态降低误订阅成本。前端通过归一化函数与组合过滤规则把解析器输出的原始元数据收敛为可操作的筛选维度后端则以 SSE 流式输出保证首屏结果即时可见。若读者希望深入可从 webui/src/components/search/ab-search-modal.vue 与 webui/src/store/search.ts 入手结合 backend/src/module/searcher/searcher.py 的流式生成器与 webui/src/api/tests/search.test.ts 的 EventSource 生命周期测试即可完整还原这条从关键词到订阅的全链路。赞分享后端前端音视频【免费下载链接】Auto_BangumiAutoBangumi - 全自动追番工具项目地址https://gitcode.com/gh_mirrors/au/Auto_Bangumi点击查看免费下载相关推荐Airweave搜索界面实时搜索与过滤的交互设计Airweave搜索界面实时搜索与过滤的交互设计 引言智能搜索的现代挑战 在当今数据爆炸的时代如何从海量信息中快速准确地找到所需内容成为企业和开发者的核心人工智能RAGAI AgentMCP 服务后端Handsontable 过滤与搜索实战外部搜索框、mark 高亮匹配与多列过滤面板Handsontable 过滤与搜索实战外部搜索框、 mark 高亮匹配与多列过滤面板 Handsontable 的 Search 插件与 Filters前端UI组件ARIAKIT Combobox 搜索过滤实战用 setValue 与 startTransition 构建响应式搜索下拉ARIAKIT Combobox 搜索过滤实战用 setValue 与 startTransition 构建响应式搜索下拉 本文基于 ARIAKIT 仓库中的UI组件前端上一篇AudioSR让所有音频重获新生一键提升至专业48kHz品质下一篇终极Fan Control风扇控制软件使用指南Windows平台散热管理完整解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询