Odoo 中文社区

    • 注册
    • 登录
    • 搜索
    • 版块
    • 标签
    • 热门
    • 用户
    • 群组

    Odoo中文社区可以通过以下两个域名访问:shine-it.net , odoo.net.cn

    由于系统升迁的原因,本论坛部分较早期的内容存在格式和链接损坏失效的问题,并非本论坛系统本身的缺陷,望谅解

    本社区没有维护任何QQ群讨论组,任何与本社区同名的QQ群讨论组的言论与本社区无关!

    开发人员可以登录gitter讨论组: http://gitter.im/odoo-china/Talk, 需要github账号

    如果您登录系统碰到问题,请在微信公众号留言:

    odoo11,12实现树状视图,odoo前端开发的实例教程,引入ztree.js

    Odoo 开发与实施交流
    2
    3
    1224
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • G
      guohuadeng 最后由 guohuadeng 编辑

      odoo的前端,比起很多平台,界面是十分优秀的,利用现有组件也能很快的实现功能。
      但一旦实际和友商PK,就会发现有很多不足,这些硬伤在PK中会大大降低你的杀伤力。
      替代文字
      树状视图算是最重要的一个,这个该是国内国际各大厂商的标配了,在财务、分组目录、地理位置等实际应用中十分常见。所以,实现父子关系的树状视图绝对是打单的重头戏,针对odoo11,12的前端大变化,我们开发了相关的模块,包括在单条记录中使用树状选择(如目录、科目),在列表、看板中使用树状列表导航查询,全部使用odoo标准的开发方式,widget实现,简单的xml设置。

      本文涉及模块可以在此下载,收费插件。希望童鞋们能学习后自行完成。

      多层级树状导航高级搜索
      字段多层级树状视图选择

      ** 先看最终效果,list, kanban, pivot, graph 中的效果**
      替代文字

      ** field中的效果。**

      替代文字

      这里写下开发过程,掌握后基本可以自己开发各种 odoo 的前端增强了。

      原生ztree外观比较丑,为了更符合 odooer的审美,用了 fontawsome来改造样式。

      • 引入 ztree.js

      • 列表做 qweb 页面

      • 列表继承原 odoo 的 FieldMany2One 组件

      • 在需要树状列表处理的相应模块写 view,继承更改字段视图。

      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 是个优秀的框架,努力让她更强大!

      用产品说话,odoo专业实施开发培训_广州尚鹏,供应链制造服装外贸家具生鲜开源ERP

      https://www.sunpop.cn

      1 条回复 最后回复 回复 引用 1
      • G
        guohuadeng 最后由 编辑

        对代码的显示没处理好,更清晰的可以看这里

        http://www.sunpop.cn/odoo_ztree_javascript_tutorial/

        0_1540834618310_bbc8d0c9-ffcf-4860-85f8-cb1df77c8503-image.png

        用产品说话,odoo专业实施开发培训_广州尚鹏,供应链制造服装外贸家具生鲜开源ERP

        https://www.sunpop.cn

        1 条回复 最后回复 回复 引用 0
        • H
          husted 最后由 编辑

          是否可以分享一下源代码?[email protected]

          1 条回复 最后回复 回复 引用 0
          • First post
            Last post