Odoo向导内Tree View按钮异常:初始不可点击,过滤后恢复正常
Odoo嵌入向导Tree View按钮渲染异常
问题现象
在向导内嵌入Tree View后,首次打开向导时,自定义按钮显示在列表底部且无法点击;执行筛选或分组操作后再取消,按钮会自动移至列表表头区域,且可正常触发点击事件。
界面状态说明
- 首次打开向导:按钮位于列表底部,点击无响应
- 应用筛选操作:执行列表筛选功能
- 取消筛选操作:撤销筛选条件
- 按钮恢复正常:按钮移至表头,点击可正常触发逻辑
相关配置与代码
Tree View配置
<record id="compatible_products_wizard_tree_view" model="ir.ui.view"> <field name="name">compatible.products.wizard.tree</field> <field name="model">compatible.products.wizard</field> <field name="arch" type="xml"> <tree string="Selected Products" create="0" edit="0" export_xlsx="false" js_class="buttton_in_tree"> <field name="mrp_id"/> <field name="product_id"/> <field name="lst_price"/> <field name="standard_price"/> <field name="qty_available"/> </tree> </field> </record>
按钮模板
<?xml version="1.0" encoding="UTF-8"?> <templates> <t t-extend="ListView.buttons" t-name="de_alfa_web_connector.buttons"> <t t-jquery=".o_list_buttons" t-operation="replace"> <div class="o_list_buttons" role="toolbar" aria-label="Main actions"> <button type="button" class="btn btn-info open_wizard_action" style="display:none;"> Create Moves </button> </div> </t> </t> <t t-extend="ListView.selection"> <t t-jquery=".o_list_select_domain" t-operation="after"> <button t-if="nbSelected > 0" type="button" class="btn btn-info open_wizard_action"> Create Moves </button> </t> </t> </templates>
JS代码
odoo.define('de_alfa_web_connector.tree_button', function (require) { "use strict"; var core = require('web.core'); var ListController = require('web.ListController'); var ListRenderer = require('web.ListRenderer'); var ListView = require('web.ListView'); var viewRegistry = require('web.view_registry'); var TreeButton = ListController.extend({ buttons_template: 'de_alfa_web_connector.buttons', events: _.extend({}, ListController.prototype.events, { 'click .open_wizard_action': '_OpenWizard', }), _OpenWizard: function (ev) { var treeView = this.renderer; var checkedRows = treeView.$("tbody input[type='checkbox']:checked").closest('tr'); var extractedData = []; checkedRows.each(function () { var rowData = {}; $(this).find('td').each(function (columnIndex) { var inputElement = $(this).find('input, select'); if (inputElement.length > 0) { rowData[columnIndex] = inputElement.val().trim(); } else { rowData[columnIndex] = $(this).text().trim(); } }); extractedData.push(rowData); }); var self = this; var lineVals = []; var promises = []; var Active_id; console.log('Extracted', extractedData); extractedData.forEach(function (data) { Active_id = data[1]; Active_id = Active_id.replace(/,/g, ''); Active_id = parseInt(Active_id); var firstSplit = data[2].split('['); var lastSplit = firstSplit[1].split(']'); var OldId = lastSplit[0]; var p_name = data[2]; var productPromise = self._rpc({ model: 'product.product', method: 'search_read', args: [[['old_id', '=', OldId]], ['id', 'name', 'uom_id', 'product_tmpl_id']], }).then(function (result) { return result[0] || false; }); promises.push(productPromise); var linePromise = Promise.all([productPromise]).then(function (results) { var product = results[0]; if (product) { var line = [0, 0, { 'product_id': product.id, 'name': p_name, 'product_uom_qty': 1, 'product_uom': product.uom_id[0], 'raw_material_production_id': Active_id, 'location_id': 15, 'location_dest_id': 8, 'picking_type_id': 41, }]; lineVals.push(line); } }); promises.push(linePromise); }); Promise.all(promises).then(function () { if (lineVals.length > 0) { var moveLineVals = lineVals.map(function (lineVal) { return lineVal[2]; }); var movePromise = self._rpc({ model: 'stock.move', method: 'create', args: [moveLineVals], }); movePromise.then(function (result) { console.log('Lines created successfully:', result); // Update move_raw_ids field of mrp.production with the created stock.move IDs var productionID = Active_id; // var productionPromise = self._rpc({ // model: 'mrp.production', // method: 'write', // args: [[productionID], { move_raw_ids: result }], // }); var productionPromise = self._rpc({ model: 'mrp.production', method: 'read', args: [[productionID], ['move_raw_ids']], }).then(function (production) { var existingMoveRawIds = production[0].move_raw_ids || []; var updatedMoveRawIds = existingMoveRawIds.concat(result); return self._rpc({ model: 'mrp.production', method: 'write', args: [[productionID], { move_raw_ids: updatedMoveRawIds }], }); }); productionPromise.then(function () { console.log('move_raw_ids field updated successfully'); self.trigger_up('history_back'); }).guardedCatch(function (error) { console.error('Failed to update move_raw_ids field:', error); }); }).guardedCatch(function (error) { console.error('Failed to create lines:', error); }); } }); }, }); var CompatibleProductsListView = ListView.extend({ config: _.extend({}, ListView.prototype.config, { Controller: TreeButton, }), }); viewRegistry.add('buttton_in_tree', CompatibleProductsListView); });
Python代码
def open_compatible_products_wizard(self): self.ensure_one() compatible_products = self.move_raw_ids_compatible_products sql = "delete from compatible_products_wizard" self.env.cr.execute(sql) m_id = self.id for item in compatible_products: wizard = self.env['compatible.products.wizard'].create({ 'mrp_id': m_id, 'product_id': item.id, }) return { 'name': 'Compatible Products', 'view_mode': 'tree', 'view_id': self.env.ref('de_alfa_web_connector.compatible_products_wizard_tree_view').id, 'res_model': 'compatible.products.wizard', 'type': 'ir.actions.act_window', 'target': 'new', }
问题修复方案
原因分析
首次渲染时,Tree View的表头按钮区域未正确初始化,自定义按钮被错误插入到列表底部;筛选/分组操作触发列表重新渲染,此时模板逻辑被正确执行,按钮插入到预期位置。
具体修复步骤
- 简化按钮模板:移除对
ListView.selection的扩展,避免模板渲染冲突,仅保留表头按钮区域的扩展:
<?xml version="1.0" encoding="UTF-8"?> <templates> <t t-extend="ListView.buttons" t-name="de_alfa_web_connector.buttons"> <t t-jquery=".o_list_buttons" t-operation="append"> <button type="button" class="btn btn-info open_wizard_action" style="display:none;"> Create Moves </button> </t> </t> </templates>
- 控制器中处理按钮显示逻辑:监听列表选择事件,动态控制按钮显示/隐藏,确保首次渲染时按钮位置正确:
// 在TreeButton控制器中添加以下代码 init: function () { this._super.apply(this, arguments); this.selectedRecords = []; }, _onSelectionChange: function (ev) { this._super.apply(this, arguments); this.selectedRecords = ev.data.ids; this.$('.open_wizard_action').toggle(this.selectedRecords.length > 0); }, events: _.extend({}, ListController.prototype.events, { 'click .open_wizard_action': '_OpenWizard', 'selection_change': '_onSelectionChange', }),
- 优化数据提取逻辑:避免通过DOM提取数据,直接使用Odoo提供的选中记录ID获取数据,提升稳定性:
_OpenWizard: function (ev) { var self = this; var selectedIds = this.selectedRecords; if (!selectedIds.length) return; this._rpc({ model: 'compatible.products.wizard', method: 'read', args: [selectedIds, ['mrp_id', 'product_id']], }).then(function (records) { // 基于读取的records处理后续逻辑,替代原DOM提取方式 records.forEach(function(record) { // 处理单个记录逻辑 }); }); }
内容的提问来源于stack exchange,提问作者DZEUF
相关产品推荐
相关产品推荐

