ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Frappe List View 模块布局:每个控件一个模块 + 一个组合模块(ADR-0002 深度解析)

Frappe List View 模块布局:每个控件一个模块 + 一个组合模块(ADR-0002 深度解析) Frappe List View 模块布局每个控件一个模块 一个组合模块ADR-0002 深度解析【免费下载链接】frappeLow code web framework for real world applications, in Python and Javascript项目地址: https://gitcode.com/GitHub_Trending/fr/frappe导读本篇基于 ui/docs/adr/0002-listview-module-layout.md 展开剖析 Frappe 前端库framework/ui位于仓库ui/目录在把 Doctype 列表视图从 CRM 提取为共享组件时的模块组织决策四个工具栏控件SortBy、Filter、ColumnSettings、QuickFilter各自独立成模块再由一个组合的ListView/模块把它们装配起来。读完你将掌握这套一控件一模块 组合模块的布局规则、每个模块的标准文件结构、纯逻辑与 UI 的分离测试策略以及组合模块与实验性List模块表格/页脚/批量操作栏之间的分工边界可直接用于理解甚至复用这套列表视图组件体系。背景从 CRM 提取列表视图的 ADR 系列framework/ui是 Frappe 仓库中面向各 Frappe 应用共享的客户端组件库见 ui/package.json 中的name: framework/ui。列表视图List View的抽取是其中一条明确的领域主线把 CRM 应用里用户用来塑造一个 doctype 列表的工具栏控件——排序、过滤、列设置、快速筛选——从 CRM 代码库中提取出来成为跨应用可复用的受控组件。这条抽取工作由一系列 ADR架构决策记录逐步推进本篇文章所讲的 ADR-0002 是其中的模块组织决策它直接承接0001-listview-controls-are-controlled-meta-driven.md控件必须是受控 元数据驱动的0003-filter-control-ports-crm-onto-shared-fields.mdFilter 控件的移植决策0004-fields-relocated-to-shared-module.md共享字段值输入组件0005-quickfilter-projects-over-shared-filter-list.mdQuickFilter 是共享过滤列表上的投影0006-columnsettings-resize-sync-reuses-frappe-ui-listview.mdColumnSettings 与表格拖拽改宽同步。领域术语Sort、Filter、Column、Field Options、Meta、View Snapshot 等在 ui/CONTEXT.md 中有权威定义阅读本文前建议先扫一遍Language一节尤其是这几条Controlled component控件只通过v-model持有自己的状态切片并接收doctype绝不触碰数据获取或持久化、Meta经共享的useDoctypeMeta拉取的 doctype 字段定义是 Field Options 的唯一来源、View Snapshot整个视图状态的可序列化快照是宿主持久化层拿到的唯一单元。核心决策一控件一模块 组合模块ADR-0002 的标题即结论——每个被提取的控件拥有自己独立的framework/ui模块文件夹SortBy/、Filter/、ColumnSettings/、QuickFilter/镜像已有的FileUpload/与FormLayout/模块与此同时另设一个组合的ListView/模块持有四个控件装配在一起的组合 shell并逐步长成真正的共享组合表格、页脚、选择横幅、useListView状态组合式函数。打开 ui/src/components 目录即可验证这一布局的最终落点SortBy/、Filter/、ColumnSettings/、QuickFilter/、ListView/五个模块与FileUpload/、FormLayout/并列存放结构完全一致。每个控件模块的标准解剖结构ADR 规定每个被提取的控件模块都包含四类内容以SortBy为例ui/src/components/SortBy文件类别内容SortBy 实例导出入口带自己导出子路径的index.tsindex.tsVue 组件控件本体.vueSortBy.vue纯函数与框架无关的.ts帮助函数orderBy.ts、getSortOptions.ts演示与测试stories/tests/子目录stories/SortBy.story.vue、tests/index.ts的导出面即模块的公开 API。以 SortBy/index.ts 为例它同时导出控件本体与纯函数帮助器方便字符串型宿主如存储 Frappeorder_by字符串的 CRM直接转换export { default as SortBy } from ./SortBy.vue; export { parseOrderBy, serializeOrderBy } from ./orderBy; export { getSortOptions } from ./getSortOptions; export type { Sort, SortByProps, SortOption } from ./types;其他三个控件模块遵循同样的形态Filter/index.ts 导出Filter组件、parseFilters/serializeFilters线格式转换、getFilterableFields从 Meta 推导可过滤字段以及getOperators/getDefaultOperator/getDefaultValue按字段类型给出算子集合与默认值QuickFilter/index.ts 导出QuickFilter组件与一组纯投影帮助函数getQuickFilterFields、quickFilterOperator、applyQuick等ColumnSettings/index.ts 导出ColumnSettings组件、serializeColumns/parseColumns映射到 frappe-ui 渲染形状、getDefaultColumns等。这些子路径在 ui/package.json 的exports字段中逐一登记./SortBy、./Filter、./QuickFilter、./ColumnSettings、./ListView、./experimental/List、./fields等消费方即可用framework/ui/SortBy这样的深路径精确导入这正是 ADR 中各自的导出子路径在打包层面对应的落地。为什么要一控件一模块而不是一个大 ListView 模块ADR 的 Considered Options 记录了被否决的方案与理由四个控件共用一个ListView/模块——被否决。理由是为了隔离性isolation每个控件独立演进、独立测试、独立按需加载代价只是更多的文件与导出。结合 ADR-0003 可以看到这种隔离的实际价值Filter 控件不是简单包装 frappe-ui 的ListFilter而是把 CRM 的Filter.vue逻辑和数据模型整体移植进库内部用共享Fields模块的值输入组件替换 CRM 自带的 Link/Duration/Rating 三件套并配备自己的算子表字符串有 equals/like/in/is…、日期有完整九个算子含between/timespan、Check 只有 equals外加_assign特例。这类高耦合的移植逻辑放在独立模块里才能被单独反复打磨而不污染其他控件。组合模块 ListView/shell 故事与状态组合组合模块 ui/src/components/ListView 是四个控件汇合的舞台。根据 ADR组合模块现在持有的是组合 shell 故事——把控件装配到同一个界面上、对着真实 doctype 验证与 CRM 的像素级一致性pixel parity——并将在后续切片中长成真正的共享组合表格、页脚、选择横幅、useListView状态组合式函数。这一演进在源码中清晰可见。ListViewShell.vue 目前不持有任何视图状态只渲染占位 chrome一个#toolbar插槽未来四个控件的家、一个#table插槽默认用 doctype 元数据渲染占位列头让访问者能直观看到 Meta 已解析成功。而 useListView.ts 已经是完整的组合式状态所有者——它把四个控件各自的组合式函数useFilters、useSort、useQuickFilter、useColumns组合成一个带命名空间的表面并向外暴露两个关键成员snapshot整个视图的可持久化状态快照filters、sort、columns、quickFilterFields四个切片任何一次真实编辑都会产生一个新对象因此宿主只需挂一个 watcher 即可保存全部状态restore(snapshot)部分恢复——只应用传入的键其余切片保持默认天然支持只重置过滤器或按切片加载。useListData(doctype, view)则是可选的取数伴侣useListData.ts把控件的 wire 投影view.filters.wire、view.sort.orderBy、view.columns.wire翻译成frappe.client.get_list的真实行数据并用frappe.client.get_count提供总数任何一个 wire 投影或页长变化都触发从第一页重取。组合模块的导出入口 ListView/index.ts 目前只导出 shell 与这两个组合式函数注释明确写道控件将在后续切片中落地——与 ADR 的阶段性描述完全吻合。装配这些组件的完整示例见 ui/src/components/ListView/stories/ListViewToolbar.vueFilter 与 QuickFilter 绑定同一个view.filters.conditionsref共享单一事实源零事件接线ColumnSettings 与表格拖拽改宽同样共享view.columns.shown宿主通过view.columns.setWidth消费 frappe-uiListHeader的columnWidthUpdated事件通过watch(view.snapshot, …)一次性完成 localStorage 自动保存真实宿主可换成自己的保存 RPC。2026-09-09 补充说明实验性 List 模块ADR 开头的 Note 记录了一次重要的后续演进2026-09-09表格、页脚和批量操作栏bulk bar作为一个独立的实验性List模块落地位于src/experimental/List/见 ui/CONTEXT.md 的 List module 一节没有放进组合模块内部。组合ListView/模块暂时保留但已不再是 desk 列表页的宿主。这个实验性模块的现状ui/src/experimental/List完整对应 CONTEXT.md 的描述List.vue——表格本体粘性表头、可排序、可拖拽改列宽、复选框选择列、行是链接自己既不取数也不存储任何东西List.vue 开头的注释原话ListFooter.vue——页脚行数与总数、Load More、页长选择ListBulkBar.vue——批量选择操作栏配套的纯函数columnTracks.ts、headerSort.ts、useColumnResize.ts、useRowSelection.ts与 6 个测试文件。实验性模块的 API 在 desk 列表页把它定稿之前可以不做任何弃用窗口地变更。它的消费方式与控件形成明确分工宿主负责布局、取行、绑定控件所编辑的同一份Column[]与Sort[]表格只负责渲染。导入路径为framework/ui/experimental/List在 ui/package.json 的 exports 中注册。分层原则UI 依赖 frappe-ui纯逻辑保持 frappe-ui-freeADR 中强调的另一条硬性规则是控件在.vue里直接复用frappe-uiAutocomplete、Popover、Button和 lucide 图标名纯逻辑放在不依赖 frappe-ui 的.ts文件里以便可以做单元测试。这条原则在源码中逐字落地。以 Filter 模块为例Filter.vue 中导入Filter的弹层定位直接复用 frappe-ui 的NestedPopover/PopoverADR-0003 明确说不重新推导定位逻辑而filters.ts、operators.ts这些纯函数文件只处理数据模型与字符串/数组转换没有任何 Vue 或 frappe-ui 依赖。排序控件是另一个好例子。orderBy.ts 提供parseOrderBy/serializeOrderBy两个纯函数完成富对象 ↔ Frappe wire 字符串的往返// modified desc, name asc → [{ fieldname: modified, direction: desc }, …] export function parseOrderBy(orderBy: string): Sort[] { … } // 反向空列表序列化为 export function serializeOrderBy(sorts: Sort[]): string { … }每个控件模块的tests/目录正是为这批纯函数而生SortBy 的 tests/orderBy.test.ts、tests/getSortOptions.test.ts、tests/useSort.test.tsFilter 的 tests/operators.test.ts、tests/filters.test.tsColumnSettings 的 tests/columns.test.ts、tests/getDefaultColumns.test.tsQuickFilter 的 tests/quickFilters.test.ts。组合模块的测试目录 ui/src/components/ListView/tests 也从最初的占位说明tests/README.md 写着第一批测试随组合useListView/序列化帮助函数到来演进为实际的sync.test.ts、snapshot.test.ts、columnSync.test.ts——正是 ADR组合模块逐步长成的过程记录。延后处理fields/ 值输入组件不预迁移ADR 还记录了一个刻意延后的决策fields/下的值输入组件Filter/QuickFilter 需要SortBy 不需要可能要从FormLayout/中提出来共享使用——但推迟到 Filter 阶段再做而不是提前搬。随后的 ADR-00040004-fields-relocated-to-shared-module.md确认了这一迁移最终完成共享的 ui/src/components/Fields 模块成为值输入组件的家Filter 控件据此替换了 CRM 自带的 Link/Duration/Rating 三件套。从 ADR-0002 的可能要做、推迟做到 ADR-0004 的已搬恰好演示了这套决策记录机制如何让延后变成受控的、有据可查的后续切片而不是模糊的 TODO。集成方式CRM 如何挂载 shell 追逐像素一致性ADR 提到 CRM 把 shell 挂载在临时开发路由上通过 vite alias link:解析framework/ui来追逐像素一致性。这条集成路径的意义在于shell 故事Shell.story.vue先于任何控件存在用来提前证明跨仓库接线——alias 解析、实时的useDoctypeMeta拉取、路由挂载——都是通的才轮到控件逐个填入。Shell 故事展示了几个值得注意的集成细节用:keydoctype强制重挂载useListView按值接收doctype不是 ref切换 doctype 时重挂载会低成本地重建状态useDoctypeMeta按 doctype 字符串缓存并且顺带重置所有控件不需要内部 reset watcher表格区域用 frappe-ui 自家的ListView/ListHeaderCRM 渲染的同一套 chrome配合view.columns.wire由serializeColumns生成让拖拽改宽和网格布局免费获得并保持与 CRM 像素一致页脚与选择横幅同样是宿主职责ListFooter通过useListData翻页ListSelectBanner承载批量操作故事里还演示了宿主声明合成列synthetic columnADR-0033 的 Record 指示器列状态由库携带、单元格由宿主绘制subsumes: status表示它吸收原始status字段——这类机制同样通过view.columns通道工作。对消费方的启示回到 ADR-0002 本身它对任何要在framework/ui中新增可复用控件的开发者给出了一条可复制的模板建独立模块目录镜像FileUpload//FormLayout/的解剖结构index.ts导出子路径.vueUI直接依赖 frappe-ui 纯.ts帮助函数零依赖stories/tests/在 ui/package.json 的 exports 中登记子路径让消费方以framework/ui/模块名深度导入控件保持受控v-model持有自己的状态切片、接收doctype、不取数不持久化需要共享状态的控件绑定同一个 refFilter/QuickFilter 共享FilterCondition[]ColumnSettings/表格拖拽共享Column[]需要跨控件聚合时新增组合模块与组合式函数如ListView/与useListView并通过snapshot/restore提供一次保存、按切片加载的持久化接缝有独立演进风险的大块组件表格/页脚/批量栏可放实验性子路径如experimental/List在宿主定稿前允许 API 无弃用窗口地变更。这套一控件一模块 组合模块 实验性子模块的三层布局既保证了控件级隔离与可测性又为跨控件共享状态预留了组合式的汇合点——这正是 ui/docs/adr/0002-listview-module-layout.md 这篇决策记录想要锁定的长期结构。【免费下载链接】frappeLow code web framework for real world applications, in Python and Javascript项目地址: https://gitcode.com/GitHub_Trending/fr/frappe创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表