Odoo12自定义弹窗实现与优化实践
1. 项目背景与需求分析
在Odoo12开发过程中,系统默认的消息提示机制存在几个明显痛点。最常见的就是使用raise抛出UserError或Warning异常时,会显示"Odoo Server Error"这样的系统错误标题,不仅视觉上不专业,更重要的是在Dialog弹窗场景下会导致表单数据丢失。这种体验对终端用户极不友好。
我在实际项目中就遇到过这样的案例:财务人员在审核付款单时,系统提示"供应商银行信息不完整",但由于使用了传统的异常抛出方式,导致已填写的20多个字段数据全部清空。这种设计缺陷直接影响了业务流程效率。
2. 技术方案设计思路
2.1 传统方案的局限性
Odoo原生提供的消息提示方式主要有:
- raise UserError('message')
- raise Warning('message')
- return { 'warning': {'title': '提示', 'message': '内容'} }
这些方式共同的缺点是:
- 样式不可定制,始终带有错误警示标识
- 弹窗关闭后无法执行回调函数
- 在复杂表单中可能引发数据丢失
2.2 自定义弹窗的优势
我们采用TransientModel(临时模型)+ 自定义视图的方案,可以实现:
- 完全可控的UI样式
- 支持自定义按钮和回调事件
- 保持主表单数据不丢失
- 可扩展的交互逻辑
3. 完整实现步骤
3.1 创建消息向导模型
在models目录下新建my_message_wizard.py:
from odoo import models, fields, api class MyMessageWizard(models.TransientModel): _name = 'my.message.wizard' _description = '自定义消息弹窗' message = fields.Text( string='消息内容', required=True, readonly=True ) @api.multi def action_confirm(self): """确认按钮回调方法""" return {'type': 'ir.actions.act_window_close'}关键点说明:
- 必须继承TransientModel而非Model
- 字段设置为readonly防止误修改
- 返回标准窗口关闭动作
3.2 设计弹窗视图
在views目录创建my_message_wizard.xml:
<odoo> <record id="my_message_wizard_form" model="ir.ui.view"> <field name="name">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-info-circle"/> 重要提示</h1> </div> <group> <field name="message" class="oe_inline" widget="html"/> </group> </sheet> <footer> <button name="action_confirm" string="确认" type="object" class="btn-primary" default_focus="1"/> </footer> </form> </field> </record> </odoo>视图设计技巧:
- 使用sheet和group规范布局
- 添加Font Awesome图标增强视觉效果
- 采用html widget支持富文本内容
- 设置default_focus让按钮自动获得焦点
3.3 注册模块资源
在__manifest__.py中添加:
'data': [ 'views/my_message_wizard.xml', ],3.4 业务逻辑调用示例
在需要提示的地方调用:
def button_validate(self): if not self.partner_id: message = self.env['my.message.wizard'].create({ 'message': ''' <p>请先选择业务伙伴!</p> <ul> <li>该提示不会中断业务流程</li> <li>表单数据将保持原状</li> </ul> ''' }) return { 'name': '缺失必填项', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'res_model': 'my.message.wizard', 'res_id': message.id, 'target': 'new', 'context': self._context, 'flags': {'mode': 'readonly'} } # 正常业务逻辑...4. 高级功能扩展
4.1 多按钮交互
改造action_confirm方法:
def action_choice(self, choice): if choice == 'confirm': self.env['res.partner'].search([...]).do_something() return {'type': 'ir.actions.act_window_close'} # 视图添加: <button name="action_choice" string="同意" type="object" args="('confirm',)"/> <button name="action_choice" string="拒绝" type="object" args="('reject',)"/>4.2 自动关闭定时器
在js文件中添加:
setTimeout(function() { $('.modal').modal('hide'); }, 5000); // 5秒后自动关闭4.3 响应式布局适配
添加CSS类:
<sheet class="msg-responsive"> <style> .msg-responsive { min-width: 300px; max-width: 80%; margin: 0 auto; } @media (max-width: 768px) { .msg-responsive { max-width: 95%; } } </style> ... </sheet>5. 常见问题排查
5.1 弹窗不显示
检查步骤:
- 确认__manifest__.py已注册XML文件
- 检查视图record id是否唯一
- 查看浏览器控制台是否有JS错误
5.2 按钮无响应
调试方法:
- 在方法开始添加_logger.debug调试输出
- 检查是否缺少@api.multi装饰器
- 确认按钮type="object"而非type="action"
5.3 样式异常
解决方案:
- 检查是否与其他模块CSS冲突
- 确保正确加载了Font Awesome
- 使用浏览器开发者工具审查元素
6. 性能优化建议
- 对频繁调用的提示内容使用缓存:
@tools.ormcache('message_content') def get_message_id(self, message_content): return self.create({'message': message_content}).id- 批量处理时避免N+1查询:
# 错误做法 for record in records: record.show_message(...) # 正确做法 message_ids = self.env['my.message.wizard'].create_batch([...])- 使用web_ajax代替完整弹窗:
odoo.define('module', function (require) { "use strict"; var core = require('web.core'); var Widget = require('web.Widget'); var MyWidget = Widget.extend({ showToast: function(message) { this.do_notify(_t("提示"), message); } }); });我在实际项目中发现,当需要连续显示多个提示时,采用通知(toast)样式比模态弹窗更合适。特别是在导出入库单这类批量操作场景下,可以显著改善用户体验。
