自定义odoo搜索栏
前言
搜索栏组件是我们经常会使用到的,且大多数时候我们会默认它存在。然而Odoo的原生searchBar似乎有些不太符合国内的使用习惯的。
我们使用Odoo Widget可以创建一个公共的组件进行组件复用,本期就先尝试封装一个公共的搜索栏组件,来看看应该如何实现吧~
本篇代码很硬核,强烈建议收藏反复观看!!
数据封装
既然想实现一个公共的搜索栏组件,搜索栏需要根据tree视图的列内容来展示搜索项,需要由接口将搜索栏的数据进行返回,这里我们模仿接口数据来直接进行封装,并可以对搜索栏数据初始化,JSON数据如下:
let search_mes= [ { tag: 'input', type: 'text', title: '客户名称', name: 'customer_name', //作为渲染搜索项class的name唯一值 value: 'test1', placeholder: '请输入' }, { tag: 'select', type: 'text', title: '项目类型', name: 'project_type', value: '个人', option: [ {text: '请选择', value: ''}, {text: '个人', selected: 'selected', value: '1'}, {text: '商业', value: '2'} ] }, { tag: 'select', type: 'text', name: 'id_information', title: '证件信息', option: [ {text: '请选择', value: ''}, {text: '个人', value: '1'}, {text: '商业', value: '2'} ] }, { tag: 'select', type: 'text', title: '客户类型', name: 'customer_type', option: [ {text: '请选择', value: ''}, {text: '企业', value: '1'} ] }, { tag: 'input', type: 'text', title: '手机号码', name: 'tele_phone', value: '14242141', placeholder: '请输入' }, { tag: 'select', type: 'text', title: '会员等级', name: 'level_of_membership', option: [ {text: '请选择', value: ''}, {text: '个人', value: '1'}, {text: '商业', value: '2'} ] }, { tag: 'input', type: 'date', title: '入会时间', name: 'initiation_time', value: '2022-07-13' }, { tag: 'select', type: 'text', title: '渠道', name: 'join_channel', option: [ {text: '请选择', value: ''}, {text: '个人', value: '1'}, {text: '商业', value: '2'} ] }, { tag: 'input', type: 'text', title: '卡号', name: 'membership_card_number', placeholder: '请输入' }, { tag: 'select', type: 'text', title: '业务选择', name: 'joining_city', option: [ {text: '请选择', value: ''}, {text: '当前业主', value: '1'}, {text: '历史业务', value: '2'}, {text: '员工', value: '3'}, {text: '业务VIP', value: '4'}, {text: '其他', value: '5'} ] } ]
数据渲染
创建search_widget.xml和search_widget.js,来对上述的JSON数据进行渲染,这里搜索栏的样式可以借助bootstrap的栅格布局。
代码如下:
<?xml version="1.0" encoding="utf-8" ?> <template id="template" xml:space="preserve"> <t t-name="search_tool_template"> <t t-js="widget"> console.log("widget", widget.widget); </t> <t t-set="input_mes" t-value="widget.searchPanel"></t> <div class="search_tools row" style="display: flex"> <t t-foreach="input_mes" t-as="input_item"> <div t-if="input_item.tag == 'input'" class="col-md-3 input_searth_style"> <span t-esc="input_item.title" style="width: 70px"></span> <input t-att-class="input_item.name" t-att-value="input_item.value" t-att-type="input_item.type" t-att-placeholder="input_item.placeholder" style="width: 150px"/> </div> <div t-elif="input_item.tag == 'select'" class="col-md-3 input_searth_style"> <span t-esc="input_item.title" style="width: 70px"></span> <select style="width: 150px" t-att-class="input_item.name"> <option t-att-value="item.value" t-esc="item.text" t-foreach="input_item.option" t-as="item" t-att-selected="item.selected"></option> </select> </div> </t> <div> <button class="btn btn-primary search_btn">查询</button> <button class="btn btn-white remove_btn" style="margin-left: 10px">重置</button> </div> </div> </t> </template>
odoo.define('search_tool', function (require) { var Widget = require('web.Widget'); var SearchTool = Widget.extend({ events: _.extend({}, Widget.prototype.events, { //定义搜索按钮点击事件 'click .search_btn': '_sidebarClicked', //定义重置按钮点击事件 'click .remove_btn': '_removeClicked', }), template: 'search_tool_template', init: function (parent, searchData) { this._super.apply(this, arguments); this.searchPanel = searchData }, _sidebarClicked: function (ev){ console.log(this) this.__parentedParent.trigger_up('reload') //可以获取父级的model }, _removeClicked:function (ev){ console.log('remove') } }) return SearchTool })
挂载至页面
根据上面的步骤,我们的一个搜索栏的组件就封装好了!接下来就是该如何挂载到页面上,对tree视图重写ListController.renderButtons方法。如下:
odoo.define('render_seacrh', function (require) { "use strict"; var ListController = require('web.ListController'); //引入search组件 var SearchTool = require("search_tool"); return ListController.extend({ renderButtons: function () { this._super.apply(this, arguments); let self = this if (this.$buttons) { //这⾥找到刚才定义的按钮和输入框 this.$buttons.find('.o_list_export_xlsx').addClass('d-none'); this.$buttons.find(".export_excel_data").on('click', this.proxy('export_excel_data')); //将步骤一的JSON数据放到此处 let search_mes= [ { tag: 'input', type: 'text', title: '客户名称', name: 'customer_name', value: 'test1', placeholder: '请输入' }, { tag: 'select', type: 'text', title: '是否是会员', name: 'is_member', value: '个人', option: [ {text: '请选择'}, {text: '个人', selected: 'selected'}, {text: '商业'} ] }, { tag: 'select', type: 'text', name: 'id_information', title: '证件信息', option: [ {text: '请选择'}, {text: '个人'}, {text: '商业'} ] }, { tag: 'select', type: 'text', title: '客户类型', name: 'customer_type', option: [ {text: '请选择'}, {text: '企业'} ] }, { tag: 'input', type: 'text', title: '手机号码', name: 'tele_phone', value: '14242141', placeholder: '请输入' }, { tag: 'select', type: 'text', title: '会员等级', name: 'level_of_membership', option: [ {text: '请选择'}, {text: '个人'}, {text: '商业'} ] }, { tag: 'input', type: 'date', title: '入会时间', name: 'initiation_time', value: '2022-07-13' }, { tag: 'select', type: 'text', title: '入会渠道', name: 'join_channel', option: [ {text: '请选择'}, {text: '个人'}, {text: '商业'} ] }, { tag: 'input', type: 'text', title: '会员卡号', name: 'membership_card_number', placeholder: '请输入' }, { tag: 'select', type: 'text', title: '入会城市', name: 'joining_city', option: [ {text: '请选择'}, {text: '当前业主'}, {text: '历史业务'}, {text: '员工'}, {text: '业务VIP'}, {text: '其他'} ] } ] // 实例化SearchTool,并挂载到this.$buttons上 this.searchTool = new SearchTool(this,search_mes) this.searchTool.appendTo(this.$buttons) } }, }); });
获取数据
在我们点击查询时也应当把搜索栏的数据内容拿出来,可以在搜索的点击事件里进行处理,在search_widget.js中扩展_sidebarClick方法和按钮重置_removeClicked方法。
_sidebarClicked: function (ev){ let params = {} //获取搜索栏数据 for (let searchItem of this.searchPanel) { params[searchItem.name] = $('.' + searchItem.name).val() } console.log(params) /** 业务逻辑 **/ } _removeClicked:function (ev){ //清空搜索栏数据 for (let searchItem of this.searchPanel) { $('.' + searchItem.name).val('') } }
最后将上述的文件引入资源项,让我们看看效果
这里主要介绍如何开发公共的Widget组件,具体的业务场景还需因人而异~