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 表单数据丢失问题
当在现有表单操作中触发弹框时,可能会遇到表单数据丢失的情况。解决方案是:
- 确保在按钮点击处理中正确返回Action:
@api.multi
def button_show_message(self):
# 先保存当前表单
self.ensure_one()
self.write({'state': 'pending'})
# 再显示消息
return self.env['my.message.wizard'].action_show_message('请确认操作')
- 在向导模型中处理完成后重新打开原表单:
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 多语言支持实现
为了使消息弹框支持多语言:
- 在模型字段中使用翻译方法:
message = fields.Text(string=_('Message'), required=True)
- 在XML视图中使用翻译标签:
<h1><i class="fa fa-exclamation-circle"/>
<t t-esc="_('Important Notice')"/>
</h1>
- 调用时传递翻译后的消息:
message = _('The record has been updated successfully')
return self.env['my.message.wizard'].action_show_message(message)
5.3 性能优化建议
-
避免在循环中频繁创建弹框,应该收集所有验证错误后一次性显示。
-
对于复杂表单,考虑使用前端验证减少服务器交互:
// 在前端JS中先进行基础验证
if (!this.$('input[name="name"]').val()) {
this.displayNotification({
title: _t("Error"),
message: _t("Name is required"),
type: 'danger'
});
return;
}
- 重用向导实例而不是频繁创建新实例:
@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

289

被折叠的 条评论
为什么被折叠?



