You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Odoo v15向导重复按钮问题及视图单次刷新需求

Odoo v15 列表视图按钮重复与初始化刷新问题

问题现象

  • 自定义ListController替换列表默认创建按钮为Confirm按钮后,首次打开视图时出现重复的Confirm和Export All按钮,底部按钮无法点击
  • 移除分组或应用过滤器后,重复按钮消失且不再出现
  • 尝试在init中调用reload()方法无效
  • 现有自动刷新代码会每秒刷新视图,需修改为仅初始化时刷新一次,同时解决按钮重复问题

现有代码

自定义列表控制器JS代码

odoo.define('slnee_appointment_core.tree_button', function (require) {
"use strict";
 var ListController = require('web.ListController');
 var ListView = require('web.ListView');
 var viewRegistry = require('web.view_registry');
 var utils = require('web.utils');
 var rpc = require('web.rpc');
 var TreeButton = ListController.extend({
   buttons_template: 'slnee_appointment_core.buttons',
   events: _.extend({}, ListController.prototype.events, {
   'click .open_wizard_action': '_OpenWizard',
 }),

 init: function (parent, context) {
        this._super.apply(this, arguments);
        console.log('this *********************' ,this);
        this.reload();
    },

 _OpenWizard: function () {
   var self = this;
   var selected_lines = self.getSelectedIds();
    return this._rpc({
            'model': 'appointment.reservation.pack',
            'method': 'add_services_to_batch',
            'args': [[], selected_lines],
        }).then(function(result){
                location.reload()
            });
 }
 });
    var SaleOrderListView = ListView.extend({
   config: _.extend({}, ListView.prototype.config, {
   Controller: TreeButton,
 }),
  });
   viewRegistry.add('button_in_tree', SaleOrderListView);
  });

按钮模板XML代码

<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-extend="ListView.buttons" t-name="slnee_appointment_core.buttons">
    <t t-jquery="button.o_list_button_add" t-operation="replace">
        <button type="button" class="btn btn-primary open_wizard_action">
            Confirm
        </button>
    </t>
    
</t>
</templates>

自动刷新相关JS代码

AbstractView扩展

odoo.define('slnee_appointment_core.AbstractView', function (require) {
"use strict";

require('web.AbstractView').include({

    init: function () {
        this._super.apply(this, arguments);

        var self = this;
        if (this.arch.attrs.class) {
            var classList = this.arch.attrs.class.split(' ');
            classList.forEach(function (className) {
                self._checkRefreshClass(className);
            });
        }
    },

    _checkRefreshClass: function (className) {
        var refreshClass = 'crnd-refresh-every-';
        if (className.includes(refreshClass)) {
            var time = className.substring(refreshClass.length);
            var interval = time.slice(0, -1);
            var dimension = time.slice(-1);
            if ((dimension === 's' || dimension === 'm') &&
                (/^\d+$/).test(interval)) {
                var dCoefficient = 1;
                if (dimension === 'm') {
                    dCoefficient = 60;
                }
                this.controllerParams.crndViewRefreshInterval =
                    parseInt(interval, 10) * dCoefficient * 1000;
            }
        }
    },
});
});

AbstractController扩展

odoo.define('slnee_appointment_core.AbstractController', function (require) {
"use strict";

require('web.AbstractController').include({

    init: function (parent, model, renderer, params) {
        this._super.apply(this, arguments);
        if (params.crndViewRefreshInterval) {
            this.crndViewRefreshInterval = params.crndViewRefreshInterval;
        }
    },

    start: function () {
        if (this.crndViewRefreshInterval) {
            this._setCRnDRefreshViewTimer();
        }
        return this._super.apply(this, arguments);
    },

    update: function (params, options) {
        var reload = options && 'reload' in options
            ? options.reload : true;
        if (this._crndViewRefreshTimer && reload) {
            this._setCRnDRefreshViewTimer(true);
        }

        return this._super.apply(this, arguments);
    },

    _setCRnDRefreshViewTimer: function (cancelPrevTimer) {
        if (cancelPrevTimer) {
            clearTimeout(this._crndViewRefreshTimer);
        }
        this._crndViewRefreshTimer = setTimeout(this.reload.bind(this),
            this.crndViewRefreshInterval);
    },
});
});

视图配置XML

<record id="appointment_reservation_pack_view_tree" model="ir.ui.view">
<field name="name">appointment.reservation.pack.tree</field>
<field name="model">appointment.reservation.pack</field>
<field name="arch" type="xml">
    <tree create="1" delete="0" expand="1"
          js_class="button_in_tree" class="crnd-refresh-every-1s">
        <field name="service_id"/>
        <field name="group_id"/>
        <field name="batch_id"/>
    </tree>
 <!--            <footer></footer>-->
</field>
 </record>

解决方案

1. 修复按钮重复与初始化刷新问题

按钮重复是因为init中过早调用reload导致视图渲染异常,将刷新逻辑移到start方法(视图初始化完成后执行),同时简化控制器代码:

odoo.define('slnee_appointment_core.tree_button', function (require) {
"use strict";
 var ListController = require('web.ListController');
 var ListView = require('web.ListView');
 var viewRegistry = require('web.view_registry');
 var rpc = require('web.rpc');

 var TreeButton = ListController.extend({
   buttons_template: 'slnee_appointment_core.buttons',
   events: _.extend({}, ListController.prototype.events, {
     'click .open_wizard_action': '_OpenWizard',
   }),

   // 视图初始化完成后仅刷新一次
   start: function () {
       var self = this;
       return this._super.apply(this, arguments).then(function() {
           self.reload({});
       });
   },

 _OpenWizard: function () {
   var selected_lines = this.getSelectedIds();
    return this._rpc({
            'model': 'appointment.reservation.pack',
            'method': 'add_services_to_batch',
            'args': [[], selected_lines],
        }).then(function(result){
                location.reload()
            });
 }
 });

 var SaleOrderListView = ListView.extend({
   config: _.extend({}, ListView.prototype.config, {
     Controller: TreeButton,
   }),
 });

 viewRegistry.add('button_in_tree', SaleOrderListView);
});

2. 移除自动刷新扩展代码

删除原AbstractView和AbstractController的自动刷新扩展,不需要依赖类名配置刷新逻辑,直接通过自定义控制器的start方法实现单次初始化刷新。同时修改视图配置,移除自动刷新类:

<record id="appointment_reservation_pack_view_tree" model="ir.ui.view">
<field name="name">appointment.reservation.pack.tree</field>
<field name="model">appointment.reservation.pack</field>
<field name="arch" type="xml">
    <tree create="1" delete="0" expand="1" js_class="button_in_tree">
        <field name="service_id"/>
        <field name="group_id"/>
        <field name="batch_id"/>
    </tree>
</field>
 </record>

3. 模板优化(可选)

为避免依赖原按钮存在导致的替换异常,直接重写按钮区域:

<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-extend="ListView.buttons" t-name="slnee_appointment_core.buttons">
    <t t-jquery=".o_list_buttons" t-operation="replace">
        <div class="o_list_buttons">
            <button type="button" class="btn btn-primary open_wizard_action">
                Confirm
            </button>
            <button type="button" class="btn btn-secondary o_list_button_export">
                Export All
            </button>
        </div>
    </t>
</t>
</templates>

内容的提问来源于stack exchange,提问作者DZEUF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:37:15