Odoo | 硬核组件开发!全文没一句废话!


Odoo
神州数码云基地
在 Odoo 上的尝试、调研与分享
本期内容
Widget组件开发
搜索栏组件是我们经常会使用到的,且大多数时候我们会默认它存在。然而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的栅格布局。
代码如下:
"1.0" encoding="utf-8" ?>
<template id="template" xml:space="preserve">
"search_tool_template">
"widget">
console.log("widget", widget.widget);
set="input_mes" t-value="widget.searchPanel">
"search_tools row" style="display: flex">
"input_mes" t-as="input_item">
if="input_item.tag == 'input'" class="col-md-3 input_searth_style">
"input_item.title" style="width: 70px">
"input_item.name" t-att-value="input_item.value" t-att-type="input_item.type" t-att-placeholder="input_item.placeholder" style="width: 150px"/>
"input_item.tag == 'select'" class="col-md-3 input_searth_style">
"input_item.title" style="width: 70px">
"width: 150px" t-att-class="input_item.name"> "item.value" t-esc="item.text" t-foreach="input_item.option" t-as="item" t-att-selected="item.selected">
"btn btn-primary search_btn">查询
"btn btn-white remove_btn" style="margin-left: 10px">重置
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组件哦
具体业务场景还需因人而异~
有更好的办法或疑问请
⬇欢迎加入社群一起讨论哦⬇
本期作者
前端开发工程师 丁涛