odoo18集成echarts
1.下载echarts.min.js
ECharts的官方下载网址:https://echarts.apache.org/zh/download.html
2.目录结构
3.通过tdx获取数据
def get_k_data(self, code, start, end):
"""
日线级别k线获取函数
"""
for retry in range(3):
try:
if self.api.connect(self.ip, int(self.port)):
df = self.api.get_k_data(code, start, end)
return df.to_dict('records') or []
except Exception as e:
time.sleep(3)
return []
4.引用echarts.min.js
manifest.py
'assets': {
'web.assets_backend': [
'quantization_base/static/lib/**/*',
'quantization_base/static/src/**/*',
]
},
5.前端代码实现
stock_pe_report.js
/** @odoo-module **/
import {registry} from "@web/core/registry";
import {Component, onMounted, useRef} from "@odoo/owl";
export class PeChart extends Component {
static template = "quantization_base.StockPeReport";
setup() {
this.date_list = this.props.action.params.date_list || {};
this.data_list = this.props.action.params.data_list || [];
this.peContainerRef = useRef("PeContainer"); // 通过 ref 获取 DOM 元素
onMounted(() => this.initECharts()); // 确保 DOM 已挂载
}
initECharts() {
// 确保 DOM 元素存在
if (!this.peContainerRef.el) return;
// 初始化图表
// 基于准备好的 DOM,初始化 echarts 实例
var myChart = echarts.init(this.peContainerRef.el);
// 指定图表的配置项和数据
var option = {
title: {
text: 'PE曲线图'
},
tooltip: {
trigger: 'axis'
},
legend: {
data: ['收盘价', 'PE10', 'PE20', 'PB30', 'PB40', 'PB50'],
bottom: 15, // 设置图例距离容器底部的距离
orient: 'horizontal' // 设置图例为水平排列
},
xAxis: {
type: 'category',
boundaryGap: false,
data: this.date_list
},
yAxis: {
type: 'value'
},
series: this.data_list
// 注意:如果你没有特定的颜色要求,可以移除每个 series 中的 itemStyle 配置。
};
myChart.setOption(option);
}
}
registry.category("actions").add("stock_pe_report", PeChart);
}
stock_pe_report.xml
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="quantization_base.StockPeReport">
<div id="pe-container" t-ref="PeContainer" style="width: 600px;height:400px;">
</div>
</t>
</templates>
菜单调用
stock_pe_report.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="form_stock_pe_report" model="ir.ui.view">
<field name="name">Stock PE Report</field>
<field name="model">stock.pe.report</field>
<field name="arch" type="xml">
<form>
<group>
<field name="stock_id" domain="[('finance_ids', '!=', False)]"/>
</group>
<footer>
<button string="取消" class="oe_link" special="cancel"/>
or
<button name="action_open" string="确定" type="object" default_focus="1"
class="oe_highlight"/>
</footer>
</form>
</field>
</record>
<record id="action_stock_pe_report" model="ir.actions.act_window">
<field name="name">PE/PB报表</field>
<field name="res_model">stock.pe.report</field>
<field name="view_mode">form</field>
<field name="view_id" ref="form_stock_pe_report"/>
<field name="target">new</field>
</record>
</data>
</odoo>
stock_pe_report.py
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import UserError
from datetime import datetime, timedelta
from odoo.tools import groupby
from operator import itemgetter
class StockPeReport(models.TransientModel):
_name = 'stock.pe.report'
_description = "Stock Pe Report"
stock_id = fields.Many2one('equity.stock', string="股票", required=True)
pe_pb_choice = fields.Selection([
('pe', '市盈率'),
('pb', '市净率'),
], string="P/E或P/B", default='pe', required=True)
def get_stock_trend_ids(self):
"""
获取股票数据
"""
domain = [
('stock_id', '=', self.stock_id.id),
]
stock_trend_ids = self.env['stock.trend'].search(domain, order='trend_date')
return stock_trend_ids
def action_open(self):
"""
打开报表
"""
# 打印表行
if self.pe_pb_choice != 'pe':
raise UserError('暂不支持P/B报表')
date_list, data_list = self.get_lines()
return {
'type': 'ir.actions.client',
'tag': 'stock_pe_report',
'name': _('PE报表'),
'params': {
'date_list': date_list,
'data_list': data_list
}
}
def get_lines(self):
"""
data: ['收盘价', 'PE10', 'PE20', 'PB30', 'PB40', 'PB50'],
{
name: '收盘价',
type: 'line',
data: [120, 132, 101, 134, 90, 230]
},
"""
self.ensure_one()
stock_trend_ids = self.get_stock_trend_ids()
if not stock_trend_ids:
return
date_list = []
close_trend_data = {
'name': '收盘价',
'type': 'line',
'data': []
}
pe_10_trend_data = {
'name': 'PE10',
'type': 'line',
'data': []
}
pe_20_trend_data = {
'name': 'PE20',
'type': 'line',
'data': []
}
pe_30_trend_data = {
'name': 'PB30',
'type': 'line',
'data': []
}
pe_40_trend_data = {
'name': 'PB40',
'type': 'line',
'data': []
}
pe_50_trend_data = {
'name': 'PB50',
'type': 'line',
'data': []
}
for trend_id in stock_trend_ids:
net_profit = self.stock_id.finance_ids[0].net_profit
shareholding_equity = self.stock_id.finance_ids[0].shareholding_equity
if shareholding_equity == 0:
continue
date_list.append(trend_id.trend_date.strftime('%Y-%m-%d'))
close_price = trend_id.close
pe_10_price = 10 * net_profit / shareholding_equity
pe_20_price = 20 * net_profit / shareholding_equity
pe_30_price = 30 * net_profit / shareholding_equity
pe_40_price = 40 * net_profit / shareholding_equity
pe_50_price = 50 * net_profit / shareholding_equity
close_trend_data['data'].append(close_price)
pe_10_trend_data['data'].append(pe_10_price)
pe_20_trend_data['data'].append(pe_20_price)
pe_30_trend_data['data'].append(pe_30_price)
pe_40_trend_data['data'].append(pe_40_price)
pe_50_trend_data['data'].append(pe_50_price)
return date_list, [close_trend_data, pe_10_trend_data, pe_20_trend_data, pe_30_trend_data, pe_40_trend_data, pe_50_trend_data]
ui_menu_views.xml
<menuitem action="action_stock_pe_report"
name="Pe/Pb报表"
id="menu_stock_pe_report"
parent="menu_stock_report_root"
sequence="5"/>
6.效果展示
7.学习总结
1、echarts在初始化时需要引入echarts.min.js,为了保证echarts被先引入,将lib放到最前面。
'assets': {
'web.assets_backend': [
'quantization_base/static/lib/**/*',
'quantization_base/static/src/**/*',
]
},
2、echarts在初始化时需要指定dom父节点,因此需要在onMounted中去实现初始化echarts