Odoo的主要功能之一是能够创建各种视图,允许数据记录以不同的形式展现并进行交互。目前提供的视图类型主要是有tree、form、kanban、calendar、pivot、graph、activity等。我们也可以创建自己需要的新类型,比如百度地图、大屏看板等,不过创建新的视图涉及的JS内容比较复杂,今天我们就不做介绍。今天我们将探讨如何在Odoo中对现有的视图类型进行扩展,以实现现有功能的增强。
在addons\web\static\src\webclient\actions\action_service.js中,有如下一段逻辑:
for (const [, type] of action.views) { if (type !== "search") { const arch = viewDescriptions[type].arch; const archDoc = domParser.parseFromString(arch, "text/xml").documentElement; const jsClass = archDoc.getAttribute("js_class"); const view = viewRegistry.get(jsClass, false) || viewRegistry.get(type, false); if (view) { views.push(view); } } }
这个说明只要不是search类型的视图,我们都可以通过js_class属性来指定一个新的视图,当然一般新的视图定义也是继承自原来的基础类型,只是增强部分功能。这里定义的增强可能只是js业务逻辑的变更,也可能会有UI界面上的变更。接下来我们通过实际的示例来看看这种设计效果。
现在我要在Form视图上的‘新建’按钮边上加一个新的按钮,并且点击按钮后会执行一段js代码。因为这里加按钮的话会改变UI,所以我们就先定义一个xml模板,继承基础的Form模板后增加新的按钮:
<?xml version="1.0" encoding="UTF-8"?> <templates xml:space="preserve"> <!-- 这里是继承原来的form视图的按钮模板,然后增加一个新的按钮,并且指定这个按钮点击时执行的js函数名称。 这里新的模板名称t-name要记住,后面在js中会用到。新的js方法名称也要记住,后面在js中会用到。--> <t t-name="web.FormView.Jsbtn" t-inherit="web.FormView" t-inherit-mode="primary" owl="1"> <xpath expr="//button[contains(@class, 'o_form_button_create')]" position="after"> <button type="button" class="btn btn-outline-primary" data-hotkey="j" t-on-click.stop="onCallJs" t-att-disabled="state.isDisabled"> ClickMe! </button> </xpath> </t> </templates>
这里定义了一个新的模板,t-name指定了新模板名称,这个在后面的js中会要用到。t-inherit指明了我们是继承自web.FormView模板,所以原来UI是什么样子我们就不用关心,我们只关注通过xpath增加的部分。t-inherit-mode是说明继承的行为,primary是创建了一个新的子模板,extension表示更改了父模板,因为我们不希望影响其它的Form视图,只对新视图增加按钮,所以我们用的是primary。在定义button中通过t-on-click指定了按钮点击时执行的js方法,这个方法在后面的js源码中需要定义,并开发自己需要的业务逻辑。
接下来我们看新视图的js代码要怎么写:
/** @odoo-module **/ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { formView } from '@web/views/form/form_view'; import { FormController } from '@web/views/form/form_controller'; const Dialog = require('web.Dialog'); //因为新的视图控制对象增加了新的方法名称,所以我们要继承原来的FormController,生成一个新的控制对象。 export class AddJsButtonFormController extends FormController { setup() { super.setup() } //这个方法名称就是你在xml定义模板时,指定的t-on-click的方法名称 async onCallJs() { //你可以在这个方法里写任何你想要的代码,比如弹出一个对话框 var dialog = new Dialog(null, { title: this.model.root.data.name, size: 'medium', $content: $('<div/>', { html: 'hi,I am Odoog.'//这里就是添加你要显示的内容 }), buttons: [{ text: "Close", close: true }] }); dialog.open(); } }; //这里指定新的模板名称,这个名称是在xml模板定义中的t-name指定。 AddJsButtonFormController.template = `web.FormView.Jsbtn`; //下面是指定新视图对象,主要还是继承了原来的form视图对象,这里变更的只有两个内容,一个是按钮模板,另一个是新的控制对象。 export const AddJsButtonFormView = { ...formView, Controller: AddJsButtonFormController, }; //给odoo注册一个新的视图名称,这个名称可以在任意的form定义中增加js_class="form_js_btn"即可。 registry.category("views").add("form_js_btn", AddJsButtonFormView);
因为我们是继承自Form视图,所以我们在js中也是继承跟form相关的对象,这里主要是form_view、form_controller,我们在FormController中增加了新的js方法,并且指定了新的模板名称。最后通过registry将新的视图名称进行了注册绑定。
最后我们把js和xml引入模块的资源包中,并安装模块。
'assets': { 'web.assets_backend': [ 'og_form_js_button/static/src/js/*', #这里会导入新视图需要的js文件和xml模板文件 ], },
当我们需要给某个Form视图增加按钮的时候,我们就可以通过xpath来对相应的视图增加js_class属性,如:
<odoo> <record id="res_partner_form_view_jsbtn" model="ir.ui.view"> <field name="name">res.partner.form.js</field> <field name="model">res.partner</field> <field name="inherit_id" ref="base.view_partner_form"/> <field name="arch" type="xml"> <xpath expr="//form" position="attributes"> <attribute name="js_class">form_js_btn</attribute> </xpath> </field> </record> </odoo>
我们给业务伙伴加了新的按钮,我们看显示效果如下:
当点击这个按钮时,会弹出一个对话框:
以上就是一个简单的关于js_class应用的实例。利用这个功能可以实现很多变态老板的变态需求,让我们在二开的路上继续前进!