最近老板提出一个需求,对数据进行汇总,最好能展示出来,后来看到蚂蚁数据可视化中的s2比较合适就做了一下,效果如图:
![](https://img-blog.csdnimg.cn/img_convert/e1e54a68660e68f4d601b717f0e3d2bd.png)
简单说一下思路:通过JS将S2引入,然后使用RPC通过调用模型中的方法获取数据,按照S2文档出入数据,然后“ir.actions.client”展示Qweb模板,将表格展示出来。
创建一个菜单(创建一个按钮也可以)
<menuitem id="menu_example_data_s2"
name="表格分析(S2)"
action="action_example_s2"
parent="menu_report_odoo"
sequence="10"
/>
创建action:使用的action是“ir.actions.client”,用来打开模板
<record id="action_example_s2" model="ir.actions.client">
<field name="name">表格分析(S2)</field>
<field name="tag">example.data.s2</field>
<field name="target">new</field>
</record>
要展示的模型:模型中返回数据格式在S2文档中会有要求,所以在JS调用方法时,将数据按照格式返回即可。
class ExampleData(models.Model):
_name = 'example.data'
_description = '示例数据'
def _get_country(self):
"""
默认为中国
:return:
"""
country_id = self.env['res.country'].search([('name', '=', '中国')], limit=1)
if country_id:
return country_id.id
order_name = fields.Char(
string='订单号'
)
country_id = fields.Many2one(
comodel_name='res.country',
string='国家',
default=_get_country
)
state_id = fields.Many2one(
comodel_name='res.country.state',
string='省份',
domain="[('country_id', '=', country_id)]"
)
city_id = fields.Many2one(
comodel_name='res.city',
string='市/县/区',
# domain="[('state_id', '=', state_id)]"
)
product_id = fields.Many2one(
comodel_name='example.product',
string='选购产品',
)
product_amount = fields.Float(
string='购买数量'
)
product_price = fields.Float(
string='产品单价',
related='product_id.price',
store=True,
readonly=False
)
purchase_date = fields.Date(
string='采购时间'
)
product_total = fields.Float(
string='购买总价',
compute='_compute_get_total_price',
store=True
)
@api.model
def create(self, vals_list):
name = vals_list.get('order_name')
if not name:
vals_list['order_name'] = self._pro_name()
return super(ExampleData, self).create(vals_list)
@api.depends('product_price', 'product_amount')
def _compute_get_total_price(self):
for rec in self:
rec.product_total = rec.product_amount * rec.product_price
def _pro_name(self):
"""
生成序列号
:return:
"""
new_code = self.env['ir.sequence'].get('example.data')
if new_code:
return new_code
else:
self.env['ir.sequence'].sudo().create({
'name': '订单记录',
'code': 'example.data',
'prefix': 'ED-%(year)s-%(month)s-%(day)s-',
'padding': 3,
'number_increment': 1,
'number_next_actual': 1,
'implementation': 'standard',
'active': True,
'company_id': False
})
new_code = self.env['ir.sequence'].get('example.data')
if new_code:
return new_code
def get_data_list(self):
"""
返回S2表格需要的数据
:return:
"""
data = []
data_ids = self.search([])
data_dict = {
'fields': {
'rows': ['country', 'province', 'order_code', 'product'],
'columns': [],
'values': ['product_amount', 'product_price']
},
'meta': [
{
'field': 'country',
'name': '国家',
},
{
'field': 'province',
'name': '省份',
},
{
'field': 'product',
'name': '产品',
},
{
'field': 'product_price',
'name': '单价',
},
{
'field': 'product_amount',
'name': '数量',
},
{
'field': 'order_code',
'name': '订单号',
},
],
'data': []
}
for rec in data_ids:
info = {
'country': rec.country_id.name,
'province': rec.state_id.name,
'product': rec.product_id.name,
'product_amount': rec.product_amount,
'product_price': rec.product_price,
'order_code': rec.order_name,
# 'action': 'action_view_detail_form',
# 'rec_id': rec.id
}
data.append(info)
data_dict['data'] = data
return data_dict
def action_view_detail_form(self, order_name):
"""
查看每条记录的form
:return:
"""
order_id = self.search([('order_name', '=', order_name)], limit=1)
view_id = self.env.ref('odoo_report.example_date_form_wizard_view').id
action = {
'name': '查看订单',
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'example.data',
'views': [(view_id, 'form')],
'view_id': view_id,
'target': 'new',
'res_id': order_id.id,
'context': {
'create': 0,
'edit': 0,
},
}
print(action)
return action
编写JS,主要就是引入S2,对数据进行处理
odoo.define('example_data.s2', function (require) {
"use strict";
var AbstractAction = require('web.AbstractAction');
var core = require('web.core');
var ExampleDataS2 = AbstractAction.extend({
# 引入Qweb模板
template: 'ExampleDataS2',
event: {},
init: function (parent, action) {
var self = this;
this._super.apply(this, arguments);
// 挡在单据跳转时可以获取到当前action的值
this.params = action.params;
},
start: function () {
var self = this;
var params = this.params
# 使用RPC获取模型中的数据
this._rpc({
model: 'example.data',
method: 'get_data_list',
args: [[]],
}).then(function (data) {
console.log('获取的数据', data)
self._show_data_list(data);
})
},
# 对数据进行处理
_show_data_list: function (data) {
var self = this;
var container = document.getElementById('example_data_s2');
const s2options = {
width: 900,
height: 450,
interaction: {
// 仅支持rows里面的
linkFields: ['order_code'],
},
conditions: {
text: [
{
field: 'product_amount',
mapping(fieldValue, data) {
return {
fill: "#ffc107",
};
}
},
{
field: 'product_price',
mapping(fieldValue, data) {
return {
fill: "#28a745",
};
}
}
],
// background: [
// {
// field: 'product_price',
// mapping(fieldValue, data) {
// if (fieldValue) {
// return {
// fill: "#28a745",
// };
// }
// }
// }
// ]
}
};
const s2 = new S2.PivotSheet(container, data, s2options);
// 链接跳转
console.log(S2.S2Event.GLOBAL_LINK_FIELD_JUMP)
s2.on(S2.S2Event.GLOBAL_LINK_FIELD_JUMP, (a) => {
console.log('data的值', a)
const {key, record} = a
const value = record['order_code']
if (value) {
return this._rpc({
model: 'example.data',
method: 'action_view_detail_form',
args: [[], value],
}).then(function (result) {
self.do_action(result);
});
} else {
return this.do_warn('警告', '禁止此操作')
}
});
s2.render();
}
});
core.action_registry.add('example.data.s2', ExampleDataS2);
})
展示模板,模板写的比较简单。
<template>
<t t-name="ExampleDataS2">
<div style="padding: 20px; background-color: #FFF">
<t t-call="example_data"/>
</div>
</t>
<t t-name="example_data">
<div>
<div id="example_data_s2" style="height: 100%; width: 100%"/>
</div>
</t>
</template>
引入Qweb以及S2
<template id="odoo_report.assets_end" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/odoo_report/static/js/s2/s2.js"/>
</xpath>
</template>
![](https://img-blog.csdnimg.cn/img_convert/9260418955021872a3dd1d4cd61c675b.png)
注意:odoo的版本为14,s2文档链接https://s2.antv.vision/manual/introduction
S2中其实还有许多功能都比较实用的,自己可以摸索一下,若有不当之处,还望指出。