ODOO集成Raphaël可视化销控视图
Raphaël 是一个用于在网页中绘制矢量图形的 Javascript 库。它使用 SVG W3C 推荐标准和 VML 作为创建图形的基础,你可以通过 JavaScript 操作 DOM 来轻松创建出各种复杂的柱状图、饼图、曲线图等各种图表,还可以绘制任意形状的图形,可以进行图表或图像的裁剪和旋转等复杂操作
虽然 D3.js 已经非常优秀,但多一个选择也不是坏事,何况理论上各种优秀的前端图形库都可以按照 ODOO 视图规范兼容共处一起飞:)
ODOO集成Raphaël可视化销控视图
Raphaël 是一个用于在网页中绘制矢量图形的 Javascript 库。它使用 SVG W3C 推荐标准和 VML 作为创建图形的基础,你可以通过 JavaScript 操作 DOM 来轻松创建出各种复杂的柱状图、饼图、曲线图等各种图表,还可以绘制任意形状的图形,可以进行图表或图像的裁剪和旋转等复杂操作
虽然 D3.js 已经非常优秀,但多一个选择也不是坏事,何况理论上各种优秀的前端图形库都可以按照 ODOO 视图规范兼容共处一起飞:)
ODOO阿里钉钉桌面和移动客户端免登认证通用模板
钉钉的免登接口是先要通过基于前端H5的JSAPI获取到code后才能进行后台服务端校验,但其PC端和手机端开发很可能是两个不同风格的小团队耦合而成,所以调用接口需要整合一下才能对外透明,也难怪后台应用针对两端的首页都可以分别进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<template id="entry" name="renjie_dingtalk entry"> <t t-call="web.layout"> <t t-set="head"> <script t-if="type == 'mobile'" type="text/javascript" src="//g.alicdn.com/dingding/open-develop/1.9.0/dingtalk.js"></script> <script t-if="type == 'pc'" type="text/javascript" src="//g.alicdn.com/dingding/dingtalk-pc-api/2.7.0/index.js"></script> </t> <script type="text/javascript"> /** * author: i@renjie.me */ var auth = { corpId: "<t t-raw="corp_id"/>", onSuccess: function(ret){ location.href = "/auth_dingtalk/sso" + location.search + "\u0026code=" + ret.code + "\u0026state=<t t-raw="state"/>"; }, onFail: function(err){} }; <t t-if="type == 'mobile'"> dd.ready(function(){ dd.runtime.permission.requestAuthCode(auth); }); </t> <t t-if="type == 'pc'"> DingTalkPC.runtime.permission.requestAuthCode(auth); </t> </script> </t> </template> |
ODOO微信用户信息同步问题
Python使用http高级requests库来对接微信开放平台和公众平台非常干净利落,主要两个小问题需要注意一下:
1、用户昵称乱码
正常拉取用户信息所返回的内容编码不太友好,读取时乱码,需要显式设定lang和encoding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
''' author: i@renjie.me ''' def get_sns_user_info(self, sns_token, openid): url = self.get_api_url("api") + "/sns/userinfo" params = dict( access_token = sns_token, openid = openid, lang = "en", ) res = requests.get(url, params=params) res.encoding = 'utf-8' ret = res.json() log = "get_sns_user_info: sns_token(%s) openid(%s) ret(%s)" % (sns_token, openid, ret) if "nickname" in ret: _logger.info(log) return ret else: _logger.error(log) |
2、用户头像时效
如只保存头像链接,若用户更换头像,原有链接将失效,需要用Binary字段存储一份图片二进制base64编码的副本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
''' author: i@renjie.me headimgurl = fields.Char(u'头像地址', readonly=True) head = fields.Binary(u"头像", attachment=True, readonly=True) ''' @api.multi def write(self, vals): if "headimgurl" in vals: if vals.get("headimgurl"): res = requests.get(vals["headimgurl"]) vals["head"] = res.content.encode('base64') else: vals["head"] = "" return super(UsersWechat, self).write(vals) |
ODOO表单视图编辑模式禁用自动聚焦
表单创建的时候默认会自动聚焦到第一个输入框,这是人性化易用性的设计,用户不用手动点鼠标主动聚焦,直接输入内容即可,比如名称
表单编辑的时候往往第一个输入框都是已经有内容的,特别是第一个最近的字段是必填项居多,这个时候自动聚焦的意义就没有新建时候大
虽然意义不大但也不影响什么,直到最近集成一个二代身份证读卡器的硬件产品,为了兼容多平台多浏览器无控件无插件,只好使用最干净绿色的USB HID输入方案。这个时候新建的自动聚焦刚好可以和二代证的自动填充完美配合,但是编辑的时候由于仅聚焦不全选会导致二次读卡的情况下内容是追加而不是覆盖。这种场景下自动聚焦就显得多余,迫切需要在编辑模式下关闭且使用原生的窗口监听来实现最新读卡信息覆盖同步:
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * author: i@renjie.me */ var FormView = require('web.FormView'); FormView.include({ autofocus: function() { var self = this; if(self.get("actual_mode") !== "edit"){ self._super.apply(self, arguments); } } }); |
ODOO模态对话框窗口拖动
年年一个大版本推进,但其前端基础库还是很古老的Bootstrap + jQuery + Underscore组合,11版本虽然对View层进行了激进的重构,也还是基于这个基架之上。同理jQuery UI交互界面库也一脉相承的延续至今,这就可以直接调用其Draggable Widget部件以最小的代价扩展原生模态对话框的拖拽功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * author: i@renjie.me */ var Dialog = require('web.Dialog'); Dialog.include({ open: function() { var self = this; var ret = self._super.apply(self, arguments); self.$modal.draggable({handle: ".modal-header"}); return ret; }, close: function() { var self = this; if(self.$modal.draggable("instance")) { self.$modal.draggable("destroy"); } self._super.apply(self, arguments); } }); |
ODOO社区版切换左侧导航菜单
社区版的菜单体系是一二三级同时分别展示于顶部和左侧,非常的直观,习惯这点后用企业版的菜单系统是非常难受的。个人认为最好的体验因该是大屏幕设备用社区版菜单,小屏幕终端用企业版菜单,关于这个课题以后在深入探讨,先回归本次主题:
1 2 3 4 5 6 7 |
<template id="webclient_bootstrap" inherit_id="web.webclient_bootstrap" name="renjie webclient_bootstrap"> <xpath expr="//nav" position="inside"> <div class="menu-toggle"> <a href="/" title="切换子菜单" class="fa fa-exchange"></a> </div> </xpath> </template> |
以上通过继承菜单对应的模版在导航区植入一个切换开关按扭的入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* author: i@renjie.me */ .navbar{ .menu-toggle{ position: absolute; top: 6px; left: 1px; a{ color: @navbar-inverse-link-color; &:hover, &:focus { background-color: @navbar-inverse-link-hover-bg; color: @nav-pills-active-link-hover-color; } } } } |
以上LESS代码定义其样式外观与一级菜单和谐保持一致并定位到合适的位置,没错,导航最左也就是第一个菜单前面的空位也就是它了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * author: i@renjie.me */ var WebClient = require('web.WebClient'); WebClient.include({ show_application: function() { var self = this; var ret = self._super.apply(self, arguments); self.$('nav .menu-toggle').click(self.proxy(self.toggle_menu)); return ret; }, toggle_menu: function(e) { var self = this; e.preventDefault(); self.$('.o_main .o_sub_menu').toggle("fast"); return false; } }); |
最后绑定上前端行为代码就可以开关自如的控制左侧导航区,同时还有一个小小的切换动画效果。至此,社区版也可以像企业版那样间接实现整个内容区都可以用来展示视图数据,特别适合那些数据多到需要用水平滚动条来左右拖动配合使用的场景
ODOO列表视图X对多字段显示(n 条记录)问题
考虑到性能和显示的原因,原生One2many多年来一直以条数内容简单显示于固定表格中,其中最为瑕疵的就是无相关数据时的0条记录,这个Many2many也一脉相承,不知道会不会成为洁癖爱好者们心中的痛:)
今天终于等到一个机会忍无可忍精炼代码一并解决名称显示和零记录问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/** * author: i@renjie.me */ var core = require('web.core'); var Model = require('web.DataModel'); var ListView = require('web.ListView'); var ColumnMany2Many = core.list_widget_registry.get('field.many2many'); ColumnMany2Many.include({ _format: function (row_data, options) { var self = this; if (_.isEmpty(row_data[this.id].value)) { return ''; }else{ row_data[this.id] = row_data[this.id + '__display']; } return self._super.apply(self, arguments); } }); core.list_widget_registry.add('field.one2many', ColumnMany2Many); ListView.List.include({ render_cell: function (record, column) { var self = this; if (column.type === 'one2many') { var value = record.get(column.id); if (value instanceof Array && !_.isEmpty(value) && !record.get(column.id + '__display')) { new Model(column.relation).call('name_get', [ value, self.dataset.get_context() ]).done(function (names) { record.set(column.id + '__display', _(names).pluck(1).join(', ')); record.set(column.id, value); }); record.set(column.id + '__display', false); } return column.format(record.toForm().data, { model: self.dataset.model, id: record.get('id') }); }else{ return self._super.apply(self, arguments); } } }); |
ODOO集成D3.js可视化位置尺寸状态视图
10版本开始视图根据multi_record布尔属性分为多记录和单记录两种,前者以列表为代表的大部分视图,后者则是表单为经典的单一视图以及神秘的Diagram视图
虽然原生的视图可以满足大部分需求,但是一些特殊的场景用专业的视图展现可以达到事半功倍的效果,如销控图、拓扑图、树状图等
D3.js是一个非常优秀的前端可视化库,其数据驱动的理念与ODOO完美互补:前者负责美丽娇艳,后者提供关系数据:)
ODOO列表视图禁止打开表单内容
正常的列表视图做为菜单动作直接打开的话,直接点击是跳转当前动作下的表单视图;还有一种是做为表单视图One2many、Many2many类型的嵌入式列表视图,直接点击打开Dialog窗口展示表单视图,一些特殊的需求场景下往往希望只将信息展示到列表即止,不用更多的详细互动:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * author: i@renjie.me */ var ListView = require('web.ListView'); ListView.List.include({ row_clicked: function () { var self = this; var context = self.dataset.get_context().eval(); if(!context["disable_open"]){ self._super.apply(self, arguments); } } }); |
原理就是找到相关的行点击入口,通过万能的context扩展一个独立的禁止打开参数,默认不禁止,视图里通过显式声明使用该功能:
1 |
<field name="renjie_ids" context="{'disable_open': True}"/> |
ODOO QWeb报表黑色实线边框表格
原生报表表格的默认边框如下:
除了抬头一横条黑线外,剩下的灰线在针式打印下约等于无,非常没有整体感
根据Bootstrap标准和Less规范精炼了一段CSS预处理代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* author: i@renjie.me */ .table-bordered{ border: 1px solid black; > thead, > tbody, > tfoot{ > tr{ > th, > td{ border: 1px solid black; } } } } .table-condensed{ .table-bordered } |
可以直接添加到源码addons/report/static/src/less/report.less最后,也可以通过间接继承report.assets_common模板视图的根位置插入一个link元素less新文件