odoo的前端,比起很多平台,界面是十分优秀的,利用现有组件也能很快的实现功能。
但一旦实际和友商PK,就会发现有很多不足,这些硬伤在PK中会大大降低你的杀伤力。
树状视图算是最重要的一个,这个该是国内国际各大厂商的标配了,在财务、分组目录、地理位置等实际应用中十分常见。所以,实现父子关系的树状视图绝对是打单的重头戏,针对odoo11,12的前端大变化,我们开发了相关的模块,包括在单条记录中使用树状选择(如目录、科目),在列表、看板中使用树状列表导航查询,全部使用odoo标准的开发方式,widget实现,简单的xml设置。
本文涉及模块可以在此下载,收费插件。希望童鞋们能学习后自行完成。
多层级树状导航高级搜索
字段多层级树状视图选择
** 先看最终效果,list, kanban, pivot, graph 中的效果**
** field中的效果。**
这里写下开发过程,掌握后基本可以自己开发各种 odoo 的前端增强了。
原生ztree外观比较丑,为了更符合 odooer的审美,用了 fontawsome来改造样式。
ztree widget是模块核心,主要代码如下:
引入ztree
<template id="assets_backend" name="odtree assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" type="text/css" href="/app_web_widget_ztree/static/src/lib/ztree_v3/css/awesomeStyle/fa.less"/>
<link rel="stylesheet" type="text/css" href="/app_web_widget_ztree/static/src/lib/ztree_v3/css/awesomeStyle/awesome.less"/>
<link rel="stylesheet" type="text/less" href="/app_web_widget_ztree/static/src/less/views.less"/>
<script type="text/javascript" src="/app_web_widget_ztree/static/src/lib/ztree_v3/js/jquery.ztree.core.js"/>
<script type="text/javascript" src="/app_web_widget_ztree/static/src/js/widget_ztree.js"/>
</xpath>
</template>
qweb template 构建需要ztree的部份
<!--常规的-->
<div t-name="App.zTree" class="ztree"
t-att-id="widget.ztree_id"
t-att-data-ztree_index="widget.ztree_index"
t-att-data-ztree_field="widget.ztree_field"
t-att-data-ztree_model="widget.ztree_model"
t-att-data-ztree_parent_key="widget.ztree_parent_key">
</div>
<!--field 中的widget-->
<t t-name="App.FieldZtree">
<t t-if="widget.mode === 'readonly'">
<a t-if="!widget.nodeOptions.no_open" class="o_form_uri" href="#"/>
<span t-if="widget.nodeOptions.no_open"/>
</t>
<div t-if="widget.mode === 'edit'" class="o_field_widget o_field_many2one">
<div class="o_input_dropdown">
<input type="text" class="o_input"
t-att-barcode_events="widget.nodeOptions.barcode_events"
t-att-tabindex="widget.attrs.tabindex"
t-att-autofocus="widget.attrs.autofocus"
t-att-placeholder="widget.attrs.placeholder"
t-att-id="widget.idForLabel"/>
<span class="o_dropdown_button" draggable="false"/>
</div>
<button type="button" t-if="!widget.nodeOptions.no_open" class="fa fa-external-link btn btn-default o_external_button" tabindex="-1" draggable="false"/>
</div>
</t>
实现widget的代码,这个主要按正常odoo js教程,在 init, start, render 几个标准widget执行过程中继承即可,多用 _super,同时按 ztree 教程设置好 setting 参数,传参生成ztree
buildTreeView: function (search_val) {
var self = this;
if (self.many2one) {
self.many2one.destroy();
self.many2one = undefined;
}
var setting = {
callback: {
onClick: function (event, treeId, treeNode, clickFlag) {
self._selectNode(event, treeNode);
}
}
};
self._search(search_val).then(function (result) {
//todo: 不能默认让node selected,会出现quick_create 混乱
if (self.value && self.value.data.id && self.value.data.id > 0)
var ztree_selected_id = self.value.data.id;
self.many2one = new zTree(setting, {
zNodes: result,
ztree_field: self.field.name,
ztree_model: self.field.relation,
ztree_parent_key: self.ztree_parent_key,
ztree_expend_level: self.ztree_expend_level,
ztree_selected_id: ztree_selected_id,
});
self.many2one.appendTo(self.$input.parent());
// self.$(".ztree").replaceWith(self.many2one);
self.$input.css('height', 'auto');
});
},
为了更像原生的 m2o ,做了很多附加代码,主要是把原生many2one的执行函数调整,用ztree方式进行赋值和点击事件处理,比如最重要的输入后查找过滤功能
_bindAutoComplete: function () {
var self = this;
this._super.apply(this, arguments);
this.$input.autocomplete({
source: function (req) {
if (!self.many2one)
self.buildTreeView(req.term);
},
focus: function (event, ui) {
event.preventDefault(); // don't automatically select values on focus
},
close: function (event, ui) {
if (event.which === $.ui.keyCode.ESCAPE) {
event.stopPropagation();
}
console.log('ui close');
},
autoFocus: true,
html: true,
minLength: 0,
delay: this.AUTOCOMPLETE_DELAY,
});
this.$input.autocomplete("option", "position", {my: "left top", at: "left bottom"});
},
最后,要在视图中使用。以产品目录的输入改为 树状视图 为例
<!-- Product -->
<record id="app_product_template_form_view" model="ir.ui.view">
<field name="name">app.product.template.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='categ_id']" position="attributes">
<!-- Add your fields or attributes here -->
<attribute name="widget">ztree_select</attribute>
<attribute name="ztree_expend_level">2</attribute>
<attribute name="limit">16</attribute>
<attribute name="order">name</attribute>
</xpath>
</field>
</record>
可以看到,核心就一个 widget="ztree_select",为了使用方便,可以做不少参数,比如默认展开的级别,排序名称等。
以上是field的处理,在list,kanban的处理会更复杂些,主要是对 search_view 操作实现过滤查询,和对 view_manager 中各种视图切换时,能实现不同的渲染,更改原生view的dom结构。
renderSuperbar: function (sender) {
//todo: 在form 中的one2many表单,也是list,此时不能render
var self = this;
//不在主视图
//如果视图不变,不处理
if (!need_render)
return false;
if (!sender.getParent().getParent().$el.hasClass('o_view_manager_content'))
return false;
//没有数据就清理
if (!self.bar_data) {
if (self.$superbar)
self.$superbar.destroy();
return false;
}
//如果不在允许的view_mode,不处理,默认只有 list, kanban
if (!self.bar_data.attrs.view_mode) {
if (self.$superbar)
self.$superbar.destroy();
return false;
}
else {
var views = self.bar_data.attrs.view_mode.split(',');
var viewTag = sender.arch.tag;
//o的list要改为tree
if (views.indexOf(viewTag) < 0) {
if (self.$superbar)
self.$superbar.destroy();
return false;
}
}
if (self.$superbar)
self.$superbar.destroy();
//一旦viewType换了,处理渲染
need_render = false;
self.$superbar = new Superbar(self.bar_data, this, sender);
self.$superbar.appendTo(sender.getParent().$el);
self.ztree_position = self.bar_data.attrs.position;
sender.getParent().$el.css('display', 'flex');
sender.getParent().$el.children('div:first').css('flex', '1');
if (self.ztree_position && self.ztree_position.toLowerCase() == 'left') {
sender.getParent().$el.children('div:first').css('order', '2');
sender.getParent().$el.children('div:last').css('border-left', '0');
} else {
sender.getParent().$el.children('div:first').css('order', '-2')
}
},
完成了 树状导航,就可以使用 view 在实际功能中定义了,比如实现订单按客户和状态来进行过滤导航。
几行xml代码搞定,你可以按自己的需要在任意的视图中增加树状导航。
```
```
odoo 是个优秀的框架,努力让她更强大!