- 需求说明
odoo 正常的查看页面字段均为只读状态,不可编辑,现阶段需求是需要在查看页面部分字段为可编辑,例如一个订单的审批页面,需要在原订单页的基础上增加上可操作的审批相关字段,而原订单内容还维持不可编辑的状态。
- 思考实现方法
1、首先创建一个用来承载审批字段与逻辑的抽象模型(models.AbstractModel), 业务表需要继承这个抽象模型,这样在业务模型里的就可以使用审批相关字段和编写的审批逻辑,然后通过视图继承,将审批相关的视图增加进原视图,这样就实现了在不改变原模型代码和视图代码的情况下,对字段及视图的扩展。后续如果有其他业务也需要实现审批功能,也可以继承此模型,实现了一定的可扩展性,将审批逻辑与业务逻辑区分开。这样此需求的基础框架已经打造好了,后面就是具体实现功能了。
2、需要突破odoo自身的限制,odoo对于视图状态mode为"readonly"或"edit"有着很多不同的逻辑,包括视图展现方式,字段的保存逻辑,当页面打开时,就会根据当前视图的状态初始化页面的展现形式,找到odoo初始化视图的方法是当下的第一步,后面还会遇到什么问题,还不可知,只能一步一步往后看...
- 开始实操
相关文件名:
订单模型:order.py name:my.order
订单视图:order_view.xml id: my_order_view
审批抽象模型:approve.py name:my.approve
继承扩展订单视图:order_approve_page.xml id: my_order_approve
- 抽象模型的创建很顺利,现在我现有的订单模型已经有了相关审批字段,打开视图,也可以看到新增的审批字段已经显示出来,只不过它们也和其他字段一样,都为只读状态,不可编辑。
- order.py文件 -> my.order模型继承my.approve
class MyOrder(models.Model): _name = 'my.order' _inherit = 'my.approve' _description = '订单' ...
- order_approve_page.xml文件 -> 视图继承
<odoo><record model="ir.ui.view" id="my_order_approve"> <field name="name">订单审批页</field> <field name="model">my.order</field> <field name="inherit_id" ref="my_order_view"/> <field name="arch" type="xml"> <!-- 增加审批相关视图 --> <xpath expr="//field[@name='wfinfo_ids']" position="before"> <div invisible="not context.get('is_approve_page')"> <div class="o_group_col_12" style="margin-top: -6px;margin-bottom: 6px;font-size: 15px;font-weight: bold; ">审批内容</div> <div style="border:1px solid #ccc; padding: 0 20px; margin-bottom: 18px;"> <group name="content" class="o_group_col_12"> <field name="db_app_approve_result" string="审批结果" /> <field name="db_app_plus_approver" string="加签审批人" options='{"no_open": True, "no_create": True}' placeholder="-请选择加签审批人"/> <field name="db_app_level_2_approver" string="二级审批"/> <field name="db_app_approval_opinions" string="审批意见" placeholder="-请填写审批意见"/> </group> <div class="o_group_col_12" style="color:red">提示:多个消息接收人ITCode之间使用英文豆号分隔,如:liminga,zhangli</div> <group class="o_group_col_12"> <field name="db_app_email_recipient" string="邮件接收人itcode" placeholder="-邮件接收人itcode"/> </group> <group class="o_group_col_12" style="text-align:center;"> <div> <button string="提交审批" name="db_app_approve" type="object" class="btn-primary" style="width:150px"/> </div> </group> </div> </div> </xpath> </field> </record> </odoo>
- 经过一番代码跟踪(苦),找到了odoo渲染不同字段类型视图和widget扩展视图的方法,我这里用到了Char,Many2one,Text还有weidget的radio,分别对应【web.basic_fields里的FieldChar、FieldRadio、FieldText、FieldMany2One】类,在其初始化方法init中,通过this.mode = 'edit',这样在odoo初始化页面时,会按照编辑页面的视图渲染逻辑处理字段,好啦,我为每个字段类型都写了一个自定义的widget,在init的方法中,增加了对mode的赋值,在视图里对应字段添加上自定义的widget,重启,升级,当我满心欢喜,以为大功告成时,看到的视图却不是我想象的样子,其中Char类型字段和Text类型字段的视图有问题,再来一番代码跟踪,发现需要在init方法中指定一下this.tagName,需要指定值为"input" 或 "text",视图才能正常显示,好吧,现在ok啦,完整代码在下面:
相关文件名:
char 类型widght:custom_field_char_keep_edit.js
radio 类型widget:custom_field_radio_keep_edit.js
text 类型widget:custom_field_text_keep_edit.js
many2one 类型widget:custom_field_many2one_keep_edit.js
tips: 具体的widget引入和使用方式请参考其他文章
- custom_field_char_keep_edit.js 文件
odoo.define('my.field_char_keep_edit', function (require) { "use strict"; var registry = require('web.field_registry'); var FieldChar = require('web.basic_fields').FieldChar; var customInputField = FieldChar.extend({ init: function (parent, name, record, options) { this._super.apply(this, arguments); this.mode = 'edit'; this.tagName = 'input'; }, _onInput: function () { this.isDirty = true; }, isSet: function () { return true; }, }); registry.add('field_char_keep_edit', customInputField); return customInputField;});
- custom_field_radio_keep_edit.js 文件
odoo.define('my.radio_keep_edit', function (require) { "use strict"; var registry = require('web.field_registry'); var FieldRadio = require('web.relational_fields').FieldRadio; var customFieldRadio = FieldRadio.extend({ init: function (parent, name, record, options) { this._super.apply(this, arguments); this.mode = 'edit'; }, isSet: function () { return true; }, }); registry.add('radio_keep_edit', customFieldRadio); return customFieldRadio;});
- custom_field_text_keep_edit.js 文件
odoo.define('my.fieldt_ext_keep_edit', function (require) { "use strict"; var registry = require('web.field_registry'); var FieldText = require('web.basic_fields').FieldText; var customFieldText = FieldText.extend({ init: function (parent, name, record, options) { this._super.apply(this, arguments); this.mode = 'edit'; this.tagName = 'textarea'; }, _onInput: function () { this.isDirty = true; }, isSet: function () { return true; }, }); registry.add('fieldtext_keep_edit', customFieldText); return customFieldText;});
- custom_field_many2one_keep_edit.js 文件
odoo.define('dboms.many2one_keep_edit', function (require) { "use strict"; var registry = require('web.field_registry'); var FieldMany2One = require('web.relational_fields').FieldMany2One; var customFieldMany2One = FieldMany2One.extend({ init: function (parent, name, record, options) { this._super.apply(this, arguments); this.isReadonly = false; this.mode = 'edit'; }, isSet: function () { return true; }, }); registry.add('many2one_keep_edit', customFieldMany2One); return customFieldMany2One;});
- 做完上面的所有步骤,咱们已经实现了需求,现在还有一个说是问题也不算问题,但有时确实是问题的问题,你会发现当你改变字段之后,odoo会自动调用write方法保存,然后reload页面,看着挺高级的,自动保存耶,省时省力,多好,但在咱们这个需求里,审批涉及的字段不算少,这样编辑一个字段就存一下,第一,用户体验不好,填一个字段就保存刷新页面,会带来明显卡顿感,第二,增加了不必要的数据库操作,如果只有一两个字段的话,这样还好,还能省去保存按钮,但是在咱们这个需求里就不太好了,那就看看能不能解决这个问题。
- 发现在页面为eidt状态时,修改字段值,就不会触发保存,而当页面为readonly时,就会触发保存操作,为啥呀,算了,跟代码看看,跟呀跟...跟呀跟...当我把变更值的方法打印出来时,发现readonly状态页面在传递参数的时候多了一个force_save=true,会不会就是他导致呢?继续跟踪代码看看.........终于在basic_controller.js文件中发现有个_onFieldChanged方法,里面判断了,当页面为readonly时,设置force_save的值为true,改改看,果然,将force_save值改为false,就并不会触发自动保存了,完美!
相关文件名:
重写basic_controller 文件:change_basic_controller.js
tips:修改其中的 _onFieldChanged 方法,引入方式参考widget,具体代码如下:
odoo.define('dboms.change_basic_controller', function (require) {"use strict";var BasicController = require('web.BasicController');var core = require('web.core');var Dialog = require('web.Dialog');var FieldManagerMixin = require('web.FieldManagerMixin');var Pager = require('web.Pager');var TranslationDialog = require('web.TranslationDialog');var _t = core._t;var ChangeBasicController = BasicController.include({ ... _onFieldChanged: function (ev) { // 获取widget名称 let widgetName = ev.target.attrs.widget; // 增加判断条件,针对部分查看页面可编辑字段需要不自动保存 if (this.mode === 'readonly' && (!widgetName || widgetName.search('keep_edit')<0)) { ev.data.force_save = true; } FieldManagerMixin._onFieldChanged.apply(this, arguments); }, ...});
- 没啦,别看啦!
版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除