Alan Hou的个人博客

Odoo 12开发之后台视图 – 设计用户界面

本文为最好用的免费ERP系统Odoo 12开发手册系列文章第十篇。

本文将学习如何为用户创建图形化界面来与图书应用交互。我们将了解不同视图类型和小组件(widgets)之间的差别,以及如何使用它们来提供更优的用户体验。

本文主要内容有:

开发准备

我们将继续使用library_checkout插件模块,它已经有了模型层,现在需要视图层来实现用户界面。本文中的代码基于第八章 Odoo 12开发之业务逻辑 – 业务流程的支持,相关代码请参见 GitHub 仓库,本章完成后代码也请参见 GitHub仓库

菜单项

用户界面的入口是菜单项,菜单项形成一个层级结构,最顶级项为应用,其下一级为每个应用的主菜单。还可以添加更深的子菜单。可操作菜单与窗口操作关联,它告诉客户端在点击了菜单项后应执行什么操作。

菜单项存储在ir.ui.menu模型中,可通过Settings > Technical > User Interface > Menu Items菜单进行查看。

library_app模块为图书创建了一个顶级菜单,library_checkout插件模块添加了借阅和借阅阶段的菜单项。在library_checkout/views/library_menu.xml文件中,借阅的菜单项 XML 代码如下:

这里有一个<menuitem>快捷元素,提供了一种定义菜单项的简写方式,比原生的<record model=”ir.ui.view”>元素要更为便捷。以上使用的属性有:

还有以下可用属性:

窗口操作(Window Actions)

窗口操作给 GUI(图形化用户界面)客户端操作指令,通常用于菜单项或视图中的按钮。它告诉 GUI 所作用的模型以及要显示的视图。这些操作可以通过域过滤器过滤出可用记录,设置默认值以及从上下文属性中过滤。窗口操作存储在ir.actions.act_window模型中,可通过Settings > Technical > Actions > Window Actions菜单进行查看。

在library_checkout/views/library_menu.xml文件中,我们可以找到借阅菜单项中使用的窗口操作,我们需要对其进行修改来启用本文中将添加的视图类型:

窗口操作通常像以上这样使用<act_window>快捷标签创建。这里修改”tree, form”为更大的列表”tree, form, activity, calendar, graph, pivot”。以上使用的窗口操作属性有:

窗口操作还有一些其它属性:

做了这些修改后,在选择Checkouts菜单项并浏览相应的列表视图时,右上角在列表和表单按钮后会增加一些按钮。但在我们创建对应视图前并不能使用,本文将一一学习。窗口操作还可在列表和表单视图的上方的 Action 菜单按钮中使用,它在 Fitlers 按钮旁。要使用这个,我们需要在元素中添加以下两个属性:

补充:此时打开借阅表单会提示Insufficient fields for Calendar View!,在编写日历视图前最好选视图模式里删除 calendar 来进行效果查看

表单视图结构

表单视图要么按照简单布局,要么按与纸质文档相似的业务文档布局。我们将学习如何设计这些业务文档布局以及使用可用的元素和组件。要进行这一学习,我们重新查看并扩展第八章 Odoo 12开发之业务逻辑 – 业务流程的支持中创建的图书借阅表单。

业务文档视图

业务应用中记录的很多数据可以按纸质文档那样展示。表单视图可模仿这些纸质文档来提供更直观的用户界面。例如,在我们的应用中,可以把一次借阅看作填写一张纸,我们将编写一个遵循这一设计的表单视图。编辑library_checkout/views/chceckout_view.xml文件并修改表单视图记录来带有业务文档视图的基本框架:

视图名称是可选的,在不写时会自动生成。为简便以上利用了这一点,在视图记录中省略了<field name=”name”>元素。可以看到业务文件视图通常使用三大区域:

底部的交流区使用了 mail 插件模块中提供的社交网络组件。可使用这些,我们的模型需要继承mail.thread和mail.activity.mixin,可参见第八章 Odoo 12开发之业务逻辑 – 业务流程的支持

头部 Header

头部header 通常用于文档所走过的生命周期或步骤,还包含相关的操作按钮。这些按钮是普通表单按钮,最重要的下一步可以高亮显示。

头部按钮

编辑表单视图中的<header>版块,我们添加一个按钮来更易于设置归还的借阅为完成(done):


这里我们在头部添加了一个Return Books 按钮,在点击时调用button_done模型方法。注意可使用class=”oe_highlight”来对用户高亮显示操作。例如,在有几个可选按钮时,我们可以高亮显示主操作或下一步要执行的“更多”操作。attrs用于在 New 和 Done 状态时隐藏该按钮。实现这点的条件使用了不会在表单显示的 state 字段。要使条件生效,我们需要将使用的所有值在网页客户端中加载。我们不打算向终端用户显示 state 字段,因此使用 invisible 将其添加为不可见字段。

ℹ️domain 或 attrs 表达式中使用的字段必须在视图中加载,作用于它们的<field>元素。如果字段不对用户可见,则必须以不可见字段元素对其进行加载。

本例中我们使用的是 state 字段,相同的效果可通过 states 字段属性实现。虽然没有 attrs 属性灵活,但它更为精简。可将 attrs 一段替换为如下代码:

attrs和states元素可见功能也可用于其它视图元素,如 field。本文后续会深入讨论。要让按钮可以运作,我们还需要实现调用的方法。在library_checkout/models/library_checkout.py file文件的借阅类里添加以下方法:

该方法首先查找 done 阶段的记录来使用,然后对 self 记录集中的每条记录,设置其 stage_id 值为完成阶段。

阶段管道

下面我们为头部添加状态条组件,显示文档所在阶段。从代码层面说,是使用statusbar组件的stage_id字段的<field>元素:

这会在头部添加一个阶段管道组件,它在表示文档当前所在生命周期点的字段上使用了statusbar组件。通常是一个状态选项字段或阶段many-to-one字段。这两类字段在 Odoo 核心模块中多次出现。clickable属性让用户可通过点击状态条来修改文档阶段。一般需要开启它,但有时又不需要,比如需要对工作进行更强的控制,并且要求用户仅使用可用的操作按钮来进入下一步。这种方法允许在切换阶段时进行指定验证。

对阶段使用状态条组件时,我们可将很少使用的阶段隐藏(折叠)在 More 阶段组中。对应的阶段模型必须要有一个标记来配置需隐藏的阶段,通常命名为 fold。然后statusbar组件使用 options 属性来将这一字段名提供给fold_field选项,如以上代码所示。

使用状态代替阶段

阶段是一个使用了模型来设置进度步骤的many-to-one字段。因此终端用户可对其动态配置来符合他们具体的业务流程以及支持看板的完美展示。我们将在图书借阅中使用到state。

状态是一个包含了流程中相当稳定步骤的选择列表,如新建、处理中和完成。终端用户无法对其进行配置,因为它是静态的,更易于在业务逻辑中使用。视图字段对状态甚至还有特别的支持:状态字段属性仅在记录处理特定状态才对用户开放。

ℹ️阶段引入的时间要晚于状态。两者现在共存,在 Odoo 内核的趋势是使用阶段来替代状态。但如前所述,状态仍提供一些阶段所不具备的功能。

可通过将阶段映射到状态中来同时获得两者的优势。在借阅模型中我们通过向借阅阶段中添加一个状态字段来实现,借阅文档通过一个关联字段来使用状态。使用状态代替阶段的模型中,我们也可以使用进度条管道。这种情况下要在进度条中列出状态,需要使用statusbar_visible属性来替换fold_field选项。具体代码如下:

注意在我们实际的图书借阅项目中并不能这么使用,因为它是阶段驱动的,而非状态驱动。

文档表单

表单画布是表单的主区域,这里放置实际的数据元素,设计上类似一张真实的纸质文档,通常 Odoo 中的这些记录也会被称为文档。通常文档表单结构包含如下区域:

文档各行通常在笔记区的第一页,在表单之后,通常有一个 chatter 组件,带有文档订阅者、讨论消息和活动规划。下面逐一了解这些区域。

补充:关于sheet的翻译Alan的理解sheet 仅为单(据),但出于行文习惯一律使用表单

标题和副标题

一个元素之外的字段不会自动带有渲染它们的标签。对于标题元素就是如此,因此该元素应用来对其进行渲染。虽然要花费额外的工作量,但这样的好处是对标签显示控制有更好的灵活性。常规 HTML,包括 CSS 样式元素,可用于美化标题。一般标题放在oe_title类中。以下为扩展后的<sheet>元素,它包含标题以及一些额外字段如副标题:

此处可以看到我们使用了div, span, h1和h3这些常规 HTML 元素。<label>元素让我们可以控制字段标签在何时何处显示。for 属性标识了获取标签文件的字段。也可以使用 string 属性来为标签提供具体的文本。本例中还使用了

我们还可在表单左上角标题旁包含展示图像。它用在 parnter 或产品这类模型的表单视图中。作为示例,我们在标题区前添加了一个member_image字段,它使用图像组件widget=”image”,以及特定的 CSS 类class=”oe_avatar”。该字段尚未添加至模型中,下面我们就来添加,我们使用关联字段来将会员的图片显示在借阅文档中。编辑library_checkout/models/library_checkout.py文件并在借阅类中添加如下字段:

表单内容分组

表单主内容区应通过<group>标签来进行组织。<group>标签在画布中插入了两列。默认在这些列中标签会在字段旁显示,因此又占据两列。字段加标签会占据 一行,下一个字段和标签又会另起一行,垂直排列。Odoo表单的常见布局是带标签的字段并排成两列。达到这一效果,我们只需要添加两个嵌入顶部的<group>标签。

继续修改表单视图,在主内容区标题<div>后添加如下代码:


为 group 标签分配name是一个好的编码实践,这样在其它模块中继承时会更易于对它们进行引用。还可设置 string 属性,一旦设置将作为该部分的标题来显示。

ℹ️Odoo 11中的修改
string 属性不能作为继承的锚点,因为在应用继承前会对其进行翻译。这时应使用 name 属性来代替它。

在 group 内,<newline>元素会强制在新的一行,下一个元素会渲染到组的第一列。附加的版块标题可通过组内<separator>元素添加,如果带有 string 属性也会显示标题标签。要更好地控制元素布局,我们可以使用col和colspan属性。

col 属性可用于<group>元素中来自定义包含的列数。如前所述,默认为两列,但可修改为任意其它数字。双数效果更佳,因为默认每个添加的字段会占据两列:字段标签和字段值。按照以下代码我们通过colspan=”2″ 来在一个组内将4个字段放在两列中显示:

以上我们使用 string 属性为组添加了标题,来更清楚地看组所在位置。注意字段的顺序不同,它们先是从左到右,然后从上到下。<group>元素可以使用 colspan 属性来设置它所占用的具体列数。默认和带标签的字段一样为两列。可以修改以上代码中 col 和 colspan 的值来在表单中查看不同的效果。比如 col=”6″ colspan=”4″的效果是什么样的?可以试一试(见下图)。

选项卡笔记本(Tabbed notebooks)

另一种组织内容的方式是 notebook 元素,一个包含多个称为页面(page)的选项卡分区的容器。它们可以让不常用的内容在不使用时隐藏起来,或者用于按话题组织大量字段。

我们将在借阅表单中添加一个带有已借图书列表的notebook 元素。在前面的<group name=”group_top”>元素后可添加如下代码:

本例中笔记本仅有一个页面。添加更多,我们需在<section>元素内添加更多的<page>版块。页面画布默认不会渲染字段标签,如需显示,需像表单主画布那样将字段放在<group>版块内。本例中我们在页面中添加了one-to-many字段line_ids,我们已经有了页面标题,因此不需要标签。page支持以下属性:

字段

视图字段有一些可用属性。大部分从模型定义中获取值,但可在视图中覆盖。以下来快速查看字段的可用属性:

一些特定字段的属性如下:

字段标签

<label>元素可用于更好地控制字段标签的展示。一个使用示例是仅在表单为编辑模式时展示标签:

这么做时,如果字段在<group>元素内部,我们通常还要对其设置nolabel=”True”。class=”oe_edit_only”可用于应用 CSS 样式,让标签仅在编辑模式下可见。

字段组件

每个字段类型都会使用相应的默认组件在表单中显示。但还有一些替代组件可以使用。对于文本字段,有如下组件:

对于数字字段,有以下组件:

对于关联和选择项字段,有以下附加组件:

ℹ️Odoo 11中的修改
state_selection在 Odoo11中引入来替换掉kanban_state_selection。后者被淘汰,但为保持向后兼容性,还支持使用。

关联字段

在关联字段中,我们可让用户操作做一些额外控制。默认用户从这些字段中创建新记录(也称作“快速创建”)并打开关联记录表单。可通过options字段属性来关闭:

context和domain也是字段属性并对于关联字段特别有用。context可定义关联字记录默认值,domain 可限制可选记录。常见的示例为让一个字段依赖其它字段值来产生选择项。domain可在模型中直接定义,但也可在视图中进行覆盖。

在to-many字段中,我们还可使用 mode 属性来更改用于显示记录的视图类型。默认为 tree,但还有其它选项:form, kanban或graph。关联字段可定义行内指定视图来使用。这些视图在元素中的嵌套视图定义中声明。例如,在line_ids借阅中,我们可以为这些线路定义特定的列表和表单视图:

线路列表将带有给定的<tree>定义。当我们与线路交互时,弹出一个表单对话框,在<form>定义中包含该结构。

小贴士:如果想要在列表视图的表单弹出窗口中直接编辑one-to-many路线,应使用<tree editable=”top”>或<tree editable=”bottom”>

按钮

按钮支持这些属性:

ℹ️Odoo 11中的修改
在 Odoo 11之前,按钮图标是来自GTK客户端库的图片,并且仅限于addons/web/static/src/img/icons中所保存图片。

ℹ️Odoo 11中的修改
在 Odoo 11中工作流引擎被淘汰并删除。此前的版本中,在支持工作流的地方,按钮可通过type=”workflow”来触发工作流引擎信号。这时name属性用于工作流的信号名。

智能按钮

在右上角版块中带有智能按钮(smart button)也很常见。智能按钮显示为带有数据指示的矩形,在点击时可进入。

Odoo 中使用的 UI样式是在放置智能按钮的地方带有一个隐藏框,按钮框通常是<sheet>的第一个元素,在<div class=”oe_title”>元素前(以及头像),类似这样:

按钮的容器是一个带有oe_button_box类的 div 元素。在 Odoo 11.0以前,可能需要添加一个oe_right类来确保按钮框在表单中右对齐。在我们的应用中,我们将在按钮中显示图书会员待归还的其它借阅的总数,点击按钮会进入这些项的列表中。

所以我们需要该会员处于 open 状态的借阅记录,排除掉当前借阅。对于按钮统计,我们应创建一个计算字段来在library_checkout/models/library_checkout.py文件的借阅类中进行计数:

下一步我们可以添加按钮框并在其中添加按钮。在<sheet>版块的上方,替换上面的按钮框占位符为以下代码:

按钮元素本身是一个带有显示数据字段的容器。这些数据是使用statinfo特定组件的普通字段。该字段通常是作用于模型中定义的计算字段。除字段外,在按钮中还可以使用静态文本,如<div>Other Checkouts</div>。其它待借阅的数量展示在按钮定义中的num_other借阅字段中。

智能按钮必须带有class=”oe_stat_button” CSS样式,并应使用 icon 属性来带有一个图标。它有一个type=”action”,表示点击按钮时将运行通过 name 属性标识的窗口操作。%(action_other_checkouts_button)d表达式返回要运行的操作的数据库 ID。

在点击按钮时,我们要查看当前会员的其它借阅列表。这可通过action_other_checkouts_button窗口操作来实现。该操作会使用合适的域过滤器打开一个图书借阅列表。操作和相应的域过滤器在表单上下文之外处理,无法访问表单数据。因此按钮必须在上下文中设置当前member_id 来供窗口操作随后使用。使用的窗口操作必须在表单之前定义,因此我们应在 XML 文件根元素<odoo>中的最上方添加以下代码:

注意我们在域过滤器中如何使用default_member_id上下文键。该键还会点击按钮链接创建新任务时为member_id字段设置默认值。域过滤器也需要当前 ID。这无需在上下文中明确设置,因为网页客户端会在active_id上下文键中自动进行设置。

以下是可在智能按钮中添加的属性,供您参考:

动态视图元素

视图元素还支持一些允许视图按字段值动态变更外观或行为的属性。我们可以有onchange 事件来在编辑表单数据时修改其它字段值,或在满足特定条件时让字段为必填或显示。

onchange 事件

onchange机制允许我在某一特定字段变更时修改其它表单字段。例如一个商品字段的 onchange可以在商品被修改时设置价格字段为默认值。在老版本中,onchange 事件在视图级别定义,但8.0之后直接在模型层中定义,无需在视图上做任何特定标记。这通过使用@api.onchange(‘field1’, ‘field2’, …) 装饰器创建模型,来对一些字段绑定 onchange 逻辑。onchange 模型方法在第八章 Odoo 12开发之业务逻辑 – 业务流程的支持中详细讨论过,其中还有相关示例。

onchange 机制还可以在用户输入时即时反馈进行计算字段的自动重算。继续使用商品来举例,如果在修改商品时价格字段变化了,它还会根据新的价格自动更新计算后的总金额字段。

动态属性

一些属性允许我们根据记录的值来动态变更视图元素的显示。指定用户界面元素的可见性可通过如下属性很方便地控制:

除这些以外,我们有一些灵活的方法来根据客户端动态生成的表达式设置元素可见性。它是一个特别属性 attrs,它的值为一个映射invisible属性值与表达式结果的字典。例如,要让closed_date字段在new和open状态时不可见,可使用如下代码:

invisible不只在字段中可用,在任意元素中均可用。例如,它可用于 notebook 页面和group元素中。attrs属性也可为其它两个属性设置值:readonly和required。它们仅对数据字段有意义,通过二者来让字段可编辑或为必填。这让我们可以实现一些基础客户端逻辑,如根据其它字段值(如 state)来让字段设为必填。

列表视图

学到这里可能不太需要介绍列表视图了,但它还一些有趣的额外属性可以讨论。下面我们修改library_checkout/views/checkout_view.xml文件来改进第八章 Odoo 12开发之业务逻辑 – 业务流程的支持中的版本:

行文本颜色和字体可根据 Python 表达式计算结果来动态变化。这通过decoration–NAME属性带上计算字段属性的表达式来实现。NAME可以是bf或it,分别表示粗体和斜体,也可以是其它Bootstrap文本上下文颜色:danger, info, muted, primary, success或warning。Bootstrap文档中有相关显示示例。

ℹ️Odoo 9中的修改
decoration-NAME 属性在 Odoo 9中引入。在 Odoo 8中使用是 colors 和 fonts 属性。

记住表达式中使用的字段必须要在<field>字段中声明,这样网页客户端才知道要从服务端获取该列。如果不想对用户显示,应对其使用invisible=”1″属性。其它 tree 元素的相关属性有:

列表视图可包含字段和按钮,表单中的大部分属性对它们也有效。在列表视图中,数值字段可显示为对应列的汇总值。为字段添加一个累加属性(sum, avg, min或max)会为其分配汇总值的标签文本。我们在 num_books 字段中添加了一个示例:

num_books字段计算每个借阅中的图书数量,它是一个计算字段,我们需要在模型进行添加:

搜索视图

可用的搜索选项通过<search>视图类型来定义。我们可以选择在搜索框中输入时自动搜索的字段。还可以预置过滤器,通过点击启用,以及在列表视图中的预置分组选项。图书借阅的搜索视图可设置如下:

在<search>视图定义中,可以看到两个member_id和user_id的简单元素,当用户在搜索框中输入时,推荐下拉框中会显示对这些字段的匹配。然后有两个使用域过滤器的预置过滤器。可在搜索框下方的 Filter 按钮下选择。第一个过滤器是 To Return 图书,也就还处于 open 状态的图书。第二个过滤器是当前图书管理员处理的图书,通过当前用户的 user_id (可在上下文的 uid 键中获取)过滤。

这两个过滤器可以分别被启用并以 OR运算符连接。以<separator />元素分隔的整块过滤器以 AND 运算符连接。

第三个过滤器仅设置 group by 上下文键,它让视图按照字段来对记录分组,本例中为 member_id 字段。

字段元素可使用如下属性:

过滤元素有以下可用属性:

其它视图类型

表单、列表和搜索视图是最常用的视图类型。但还有一些其它的视图类型可用于设计用户界面。对于前述三种基本视图类型我们已经很熟悉了,在第十一章 Odoo 12开发之看板视图和用户端 QWeb中将详细介绍看板视图,它会将记录可视化为卡片形式,甚至会按列组织为看板。下面我们将学习一些其它视图类型:

以下两种视图类型用于显示累加数据:

还有更多的视图类型,但仅在 Odoo 企业版中可用。因为我们整个系列的文章是基于社区版的,所以无法为这些视图提供示例:

官方文档中提供了对所有视图和可用属性很好的参考,这里就不再重复。我们集中于提供一些基础使用示例,这样可以对这些视图入门。这样应该可以提供一个很好的基础,然后可进一步探索每个视图的所有功能。

小贴士:可通过社区插件模块查看其它视图类型。OCA 管理的网页客户端插件请见 GitHub 仓库。例如,web_timeline模块提供了一个时间线视图类型,也可像甘特图那样展示计划信息,它是社区版的 gantt 视图类型。

活动视图

活动视图类型是内置的计划活动汇总板,帮助用于可视化活动任务。由 mail 模块提供,因此需要先安装该模块才能使用这一视图类型。要使用这一类型,只需在窗口操作的 view_code 属性的视图列表中添加活动视图类型即可。实际的视图定义会自动生成,我们也可以手动进行添加,唯一的选项是修改 string 属性,但在UI 中并不使用。

作为参考,活动视图的定义类似这样:

日历视图

从名称可以看出,该视图类型在日历中展示记录,可通过不同时间区间浏览:按月、按周或按日。以下是我们图书借阅的日历视图,根据请求日期在日历上显示各项:

补充:请记得在菜单xml文件中加回前文删除的 calendar 类型

基础的日历属性有:

ℹ️Odoo 11中的修改
dipsplay 日历属性在 Odoo 11中删除。此前的版本中,它用于自定义日历项标题文本的格式,例如display=”[name], Stage [stage_id]”。

透视表视图

还可通过透视表查看数据,它是一个动态分析矩阵。为此我们可使用透视表视图。

ℹ️Odoo 9中的修改
透视表在 Odoo 8中就已存在,作为一个图表视图功能。在 Odoo 9中,它成为一个独立的视图类型。同时也增强了透视表功能、优化了透视表数据的获取。

数据累加仅对数据库中存储的字段可用。我们将使用num_books字段来展示一些借书数量的统计。它是一个计算字段,还没有存储在数据库中。要在这些视图中使用,需要通过添加store=True属性先将其存储在数据库中:

使用如下代码来为图书借阅添加数据透视表:

图表和透视表视图应包含描述轴和度量的字段元素,两者的属性大多数都通用:

图表视图

图表视图将数据累加展示为图表,可以使用柱状图、线状图和饼图。下面来为图书借阅添加图表视图:

图表视图元素可带有一个type属性,值可为 bar(默认), pie或line。对于 bar,可使用额外的stacked=”True”属性来让柱状图叠放起来。图表使用两种类型字段:

图表和透视表视图应包含描述需使用的轴和度量的字段元素。大多数图表视图中的属性同样可在透视表视图中使用。

总结

本文中我们学习了更多创建用户界面的 Odoo 视图。我们深入讲解了表单视图,然后一起概览了其它视图类型,包括列表视图和搜索视图。我们还学习了如何向视图元素添加动态行为。

下一篇文章中,我们将学习本文中未涉及到的视图:看板视图以及它使用的模板语言 QWeb。

 

☞☞☞第十一章 Odoo 12开发之看板视图和用户端 QWeb

 

扩展阅读

以下本文中所讨论的话题的附加参考和补充材料:

 

退出移动版