Odoo12自定义消息弹框开发指南

1. 为什么需要自定义消息弹框

在Odoo12开发过程中,系统默认的消息提示机制存在几个明显的痛点。首先是样式问题,使用raise抛出UserError或Warning时,页面顶部会出现一个红色的"Odoo Server Error"提示条,这种设计在正式业务场景中显得不够专业,容易给用户造成系统出现严重错误的错觉。

其次是功能限制,当在Dialog对话框中触发系统默认提示时,经常会出现表单数据丢失的情况。特别是在处理复杂表单时,用户可能已经填写了大量数据,一个简单的验证提示就导致所有输入内容清空,这种体验对终端用户来说是灾难性的。

最后是交互体验的不足。系统默认提示无法自定义按钮文本、无法添加确认回调函数、也不能灵活控制弹窗大小和位置。这些限制使得我们在处理重要操作确认、复杂表单验证等场景时显得捉襟见肘。

2. 自定义消息弹框的实现原理

2.1 TransientModel的基础作用

自定义弹框的核心是继承models.TransientModel创建临时模型。与常规Model不同,TransientModel的数据不会永久保存到数据库中,而是在处理后自动清理。这种特性非常适合用于实现临时性的交互对话框。

class MyMessageWizard(models.TransientModel):
    _name = 'my.message.wizard'
    message = fields.Text('message', required=True)

2.2 窗口动作的触发机制

当我们需要显示弹框时,通过返回一个ir.actions.act_window动作来实现。关键参数包括:

  • view_mode: 设置为'form'表示以表单视图展示
  • target: 'new'表示在新窗口打开(实际效果是弹出对话框)
  • res_id: 指定要显示的临时记录ID
return {
    'name': '提示',
    'type': 'ir.actions.act_window',
    'view_mode': 'form',
    'res_model': 'my.message.wizard',
    'res_id': message.id,
    'target': 'new'
}

2.3 视图布局的定制化

通过XML视图定义,我们可以完全控制弹框的展示样式。基础结构包含:

  • form标签定义整体表单
  • field标签显示消息内容
  • footer中的按钮定义用户操作
<form>
    <p>
        <field name="message" readonly="1"/>
    </p>
    <footer>
        <button name="action_confirm" string="确认" type="object" 
                default_focus="1" class="oe_highlight"/>
    </footer>
</form>

3. 完整实现步骤详解

3.1 创建消息向导模型

在模块目录下新建my_message_wizard.py文件:

# -*- coding: utf-8 -*-

from odoo import models, fields, api

class MyMessageWizard(models.TransientModel):
    _name = 'my.message.wizard'
    _description = 'Custom Message Wizard'

    message = fields.Text('消息内容', required=True)
    secondary_message = fields.Text('补充说明')  # 新增的辅助消息字段
    
    @api.model
    def action_show_message(self, message, title='系统提示'):
        """ 快捷显示消息的类方法 """
        wizard = self.create({'message': message})
        return {
            'name': title,
            'type': 'ir.actions.act_window',
            'view_mode': 'form',
            'res_model': self._name,
            'res_id': wizard.id,
            'target': 'new',
            'context': self.env.context,
        }
    
    def action_confirm(self):
        """ 确认按钮的回调方法 """
        # 可以在这里添加确认后的处理逻辑
        return {'type': 'ir.actions.act_window_close'}

3.2 设计弹框视图

在views目录下创建my_message_wizard.xml:

<odoo>
    <record id="my_message_wizard_form" model="ir.ui.view">
        <field name="name">my.message.wizard.form</field>
        <field name="model">my.message.wizard</field>
        <field name="arch" type="xml">
            <form string="系统提示">
                <sheet>
                    <div class="oe_title">
                        <h1><i class="fa fa-exclamation-circle"/> 重要提示</h1>
                    </div>
                    <group>
                        <field name="message" readonly="1" class="oe_inline"/>
                        <field name="secondary_message" readonly="1" 
                               attrs="{'invisible': [('secondary_message','=',False)]}"/>
                    </group>
                </sheet>
                <footer>
                    <button name="action_confirm" string="确认" 
                            type="object" class="btn-primary"/>
                    <button string="取消" special="cancel" 
                            class="btn-secondary"/>
                </footer>
            </form>
        </field>
    </record>
</odoo>

3.3 注册文件到清单

在__manifest__.py中添加依赖和视图文件:

{
    'name': 'Custom Message Wizard',
    'version': '1.0',
    'depends': ['base'],
    'data': [
        'views/my_message_wizard.xml',
    ],
}

4. 高级应用与实战技巧

4.1 动态控制弹框样式

通过传递context参数,可以实现弹框样式的动态控制:

# 在调用时传递样式参数
return {
    'name': '警告',
    'type': 'ir.actions.act_window',
    'view_mode': 'form',
    'res_model': 'my.message.wizard',
    'res_id': message.id,
    'target': 'new',
    'context': {
        'dialog_size': 'medium',  # small/medium/large
        'dialog_class': 'bg-warning',  # 背景色类
    }
}

然后在XML视图中通过t-if条件渲染不同样式:

<div t-if="context.get('dialog_class')" 
     t-attf-class="alert #{context.get('dialog_class')}">
    <!-- 内容 -->
</div>

4.2 多按钮与回调处理

扩展向导模型,支持多个操作按钮:

def action_approve(self):
    # 批准逻辑
    self.env.user.notify_success('操作已批准')
    return {'type': 'ir.actions.act_window_close'}

def action_reject(self):
    # 拒绝逻辑
    return {
        'type': 'ir.actions.act_window',
        'view_mode': 'form',
        'res_model': 'reject.reason.wizard',  # 另一个向导模型
        'target': 'new'
    }

对应视图添加多个按钮:

<footer>
    <button name="action_approve" string="批准" type="object" 
            class="btn-success"/>
    <button name="action_reject" string="拒绝" type="object" 
            class="btn-danger"/>
    <button string="取消" special="cancel" class="btn-secondary"/>
</footer>

4.3 与前端JavaScript交互

在按钮上添加JS事件处理:

<button name="action_confirm" string="确认" type="object" 
        class="oe_highlight" 
        attrs="{'data-action': 'custom_action'}"/>

然后通过扩展Widget实现前端交互:

odoo.define('my_module.CustomDialog', function(require) {
    "use strict";
    
    var Widget = require('web.Widget');
    
    var CustomDialog = Widget.extend({
        template: 'CustomDialogTemplate',
        events: {
            'click button[data-action]': '_onActionButtonClick',
        },
        
        _onActionButtonClick: function(ev) {
            var $button = $(ev.currentTarget);
            var action = $button.data('action');
            // 自定义处理逻辑
        }
    });
    
    return CustomDialog;
});

5. 常见问题与解决方案

5.1 表单数据丢失问题

当在现有表单操作中触发弹框时,可能会遇到表单数据丢失的情况。解决方案是:

  1. 确保在按钮点击处理中正确返回Action:
@api.multi
def button_show_message(self):
    # 先保存当前表单
    self.ensure_one()
    self.write({'state': 'pending'})
    # 再显示消息
    return self.env['my.message.wizard'].action_show_message('请确认操作')
  1. 在向导模型中处理完成后重新打开原表单:
def action_confirm(self):
    # 获取原始记录ID
    original_id = self.env.context.get('active_id')
    return {
        'type': 'ir.actions.act_window',
        'view_mode': 'form',
        'res_model': 'original.model',
        'res_id': original_id,
        'target': 'current'
    }

5.2 多语言支持实现

为了使消息弹框支持多语言:

  1. 在模型字段中使用翻译方法:
message = fields.Text(string=_('Message'), required=True)
  1. 在XML视图中使用翻译标签:
<h1><i class="fa fa-exclamation-circle"/> 
    <t t-esc="_('Important Notice')"/>
</h1>
  1. 调用时传递翻译后的消息:
message = _('The record has been updated successfully')
return self.env['my.message.wizard'].action_show_message(message)

5.3 性能优化建议

  1. 避免在循环中频繁创建弹框,应该收集所有验证错误后一次性显示。

  2. 对于复杂表单,考虑使用前端验证减少服务器交互:

// 在前端JS中先进行基础验证
if (!this.$('input[name="name"]').val()) {
    this.displayNotification({
        title: _t("Error"),
        message: _t("Name is required"),
        type: 'danger'
    });
    return;
}
  1. 重用向导实例而不是频繁创建新实例:
@api.model
def get_message_wizard(self):
    # 查找或创建单例实例
    wizard = self.env['my.message.wizard'].search([], limit=1)
    if not wizard:
        wizard = self.create({'message': ''})
    return wizard
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值