odoo 网站使用 ajax请求 通过自定义小部件显示第三方数据
起因:Odoo的网站或后台,是公司成员花费最多时间的地方,在 odoo 网站的首页,动态显示第三方接口中的数据。
实现思路:Odoo 网页请求都是由 Python 库 werkzeug 来进行处理的,但是还没有学会这个模块的使用方法,于是通过 ajax 折中的先实现这个效果。
Odoo 自带完整的内容管理系统,通过 拖拽 ,终端用户可以在几分钟内做出一个静态页面。本文的思路就是用它的这个 拖拽 的功能,将数据做好放在 拖拽 的模块中,在用户 拖拽 前,实际上数据已经全部加载完成了,拖拽 出来的就是一个已经把数据渲染出来的代码块,一个 html 结构。
文章中使用的环境是 Odoo 13 + Postgres 12 + Docker,文章中的代码都依赖 website 模块,通过一个自定义的 “小部件” 实现这个小功能。
如果有不会搭建这个环境的,请看这篇文章:Docker 搭建 Odoo 13 本地环境
拖拽 小部件结构:
文章中示意图的尺寸是: 100x80 px
用 PS 或 Sketch 把图做好,同官方提供的示意图类似就行,Odoo 网站后台的图形界面,都是由一块一块的 “小部件” 组成,全部是拖拽的方法直接使用的。
编写小部件结构示意图
在 views 中添加 snippets.xml,snippets 就是可以自定义的拖拽插件。在这个 xml 文件中,需要做以下3件事情:
- 配置 拖拽 小部件的预览图以及小部件名称
- 编写 小部件的 html 结构
- 配置 小部件的 Options 选项
小部件的预览图:
// t-thumbnail 预览图片地址
// t-snippet 小组件结构
<template id="news" name="news" inherit_id="website.snippets">
<xpath expr="//div[@id='snippet_structure']/div[hasclass('o_panel_body')]" position="inside">
<t t-snippet="theme_a.s_news" t-thumbnail="/theme_anviz/static/src/img/news.jpg" />
</xpath>
</template>
小部件代码结构:
// 小部件的 名称与id 缺一不可
<template id="s_news" name="News"></template>
小部件 Options 配置:
<t t-call="web_editor.snippet_options"/>
<!-- COLOR | .s_news -->
<div data-js="colorpicker"
data-selector=".s_news .row"
data-target=".card"
data-palette-title="Box Color">
<we-collapse-area>
<we-toggler><i class="fa fa-fw fa-eyedropper"/> Color</we-toggler>
<we-collapse/>
</we-collapse-area>
</div>
<!-- V-ALIGN -->
<div id="row_valign_snippet_option" data-selector=".s_news" data-target=".row">
<we-collapse-area>
<we-toggler><i class="fa fa-fw fa-arrows-v"/> Alignment</we-toggler>
<we-collapse>
<we-button data-select-class="align-items-start">Top</we-button>
<we-button data-select-class="align-items-center">Middle</we-button>
<we-button data-select-class="align-items-end">Bottom</we-button>
<we-divider/>
<we-button data-select-class="align-items-stretch">Equal height</we-button>
</we-collapse>
</we-collapse-area>
</div>
需要注意下面两点,分别对应小部件结构中的 class ,拖拽动作是通过这两个属性去触发
- data-selector
-
data-target
加载小部件 css/js 资源文件
本文中的小部件资源,均加载在网站的所有资源末尾中 web.assets_frontend,与 theme 资源一起加载。
<template id="assets_frontend" name="anviz assets" inherit_id="web.assets_frontend">
<xpath expr="link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/theme_anviz/static/src/css/swiper.min.css"/>
<link rel="stylesheet" type="text/scss" href="/theme_anviz/static/src/scss/cur.scss"/>
<link rel="stylesheet" type="text/scss" href="/theme_anviz/static/src/css/font.css"/>
</xpath>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/theme_anviz/static/src/js/get_data.js"/>
</xpath>
</template>
小常识,odoo 资源包分类:
- web.assets_common:包含所有应用的基本工具文件,如 jquery 等,此类资源用于网站、后台、销售等各类模块
- web.assets_backend:odoo 后台使用,包含所有 Web 客户端、Views等管理器的各类模块
- web_editor.assets_editor和web_editor.summernote:包含网站小部件拖拽功能的资源代码
- web.assets_frontend:包含所有网站相关资源
- web.report_assets_common: 报表资源
小部件功能涉及四个文件,全部代码如下:
- static 中的 css&js
- views 中的 assets.xml & snippets.xml
odoo模块系统通过在全局odoo对象上定义函数define来工作,odoo.define() 会默认自动执行,同 $(funciton(){...}) 一样的。
odoo.define( moduleName, dependencies, function( require, factory ){ ... }) 有三个参数
moduleName 是必须的唯一的字符串,习惯写法:addons 中的插件名称 . js 名称 : theme_a.get_data.js
dependencies 是可选的,是 js 模块的依赖项
最后一个是返回函数
odoo.define('theme_a.get_news_data.js', function(require, factory) {
'use strict';
$.ajax({
url: 'https://www.xxx.com/news/index/json',
type: "GET",
crossDomain: true,
datatype: 'application/json',
success: function(data){
console.log(data)
var list = data.data.DATA;
for(var i = 0; i < list.length; i++){
var n = list[i];
var newDate = n.public_time;
var newTitle = n.title;
var newUrl = n.url;
var newDes = n.description;
var newPic = n.thumb;
var newsList = $('#wrap').find('.s_news .list');
var itemCard = $(`<div class="col-lg-4 col-md-4 col-sm-4 col-xs-12 pt16 pb16"><div class="card"></div></div>`);
var itemCardBody =
$(`<div class="card-body">
<div class="card-text">${newDate}</div>
<h3 class="card-title">${newTitle}</h3>
<p class="card-text">${newDes}</p>
<a href="${newUrl}" class="news-learn-more anviz-btn anviz-primary-btn">Learn More</a>
</div>`);
var itemImg = $(`<img class="card-img-top" src="${newPic}" />`);
itemCard.append(itemImg)
itemCard.append(itemCardBody)
newsList.append(itemCard);
}
},
error:function(err){
console.log(err)
}
});
});
//assets.xml
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<template id="assets_frontend" name="anviz assets" inherit_id="web.assets_frontend">
<xpath expr="link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/theme_anviz/static/src/css/swiper.min.css"/>
<link rel="stylesheet" type="text/scss" href="/theme_anviz/static/src/scss/cur.scss"/>
<link rel="stylesheet" type="text/scss" href="/theme_anviz/static/src/css/font.css"/>
</xpath>
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/theme_anviz/static/src/js/get_data.js"/>
</xpath>
</template>
</odoo>
//snippets.xml
<?xml version='1.0' encoding='utf-8'?>
<odoo>
<template id="news" name="news" inherit_id="website.snippets">
<xpath expr="//div[@id='snippet_structure']/div[hasclass('o_panel_body')]" position="inside">
<t t-snippet="theme_anviz.s_news" t-thumbnail="/theme_anviz/static/src/img/news.jpg" />
</xpath>
</template>
<template id="s_news" name="News">
<section class="s_news bg-200 pt100 pb32">
<div class="container">
<div class="list row">
<div class="col-lg-4 col-md-4 col-sm-4 col-xs-12 pt16 pb16">
<div class="card">
<img class="card-img-top" src="" />
<div class="card-body">
<div class="card-text"></div>
<h3 class="card-title"></h3>
<p class="card-text"></p>
<a href="" class="news-learn-more anviz-btn anviz-primary-btn" target="_blank">Learn More</a>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<template id="snippet_options">
<t t-call="web_editor.snippet_options"/>
<!-- COLOR | .s_news -->
<div data-js="colorpicker"
data-selector=".s_news .row"
data-target=".card"
data-palette-title="Box Color">
<we-collapse-area>
<we-toggler><i class="fa fa-fw fa-eyedropper"/> Color</we-toggler>
<we-collapse/>
</we-collapse-area>
</div>
<!-- V-ALIGN -->
<div id="row_valign_snippet_option" data-selector=".s_news" data-target=".row">
<we-collapse-area>
<we-toggler><i class="fa fa-fw fa-arrows-v"/> Alignment</we-toggler>
<we-collapse>
<we-button data-select-class="align-items-start">Top</we-button>
<we-button data-select-class="align-items-center">Middle</we-button>
<we-button data-select-class="align-items-end">Bottom</we-button>
<we-divider/>
<we-button data-select-class="align-items-stretch">Equal height</we-button>
</we-collapse>
</we-collapse-area>
</div>
<!-- Accordion -->
<div data-js="collapse"
data-selector='.s_news > .card'
data-drop-in='.accordion:has(> .card)'/>
<div data-js="layout_column"
data-selector="section"
data-target="> * > .row:not(.s_nb_column_fixed)">
<we-collapse-area>
<we-toggler><i class="fa fa-fw fa-columns"/> Number of columns</we-toggler>
<we-collapse data-no-preview="true">
<we-button data-select-count="1">1</we-button>
<we-button data-select-count="2">2</we-button>
<we-button data-select-count="3">3</we-button>
<we-button data-select-count="4">4</we-button>
<we-button data-select-count="5">5</we-button>
<we-button data-select-count="6">6</we-button>
</we-collapse>
</we-collapse-area>
</div>
</template>
</odoo>
用作记录。