odoo前端的Patch用法
一、Patching code:根据官方功能解释
我们需要自定义 UI 的工作方式。一些受支持的 API 涵盖了许多常见需求。
例如,所有注册表都是很好的扩展点: 字段注册表允许添加/删除专门的字段组件,或者主组件注册表允许添加应该一直显示的组件。
但是,在某些情况下它是不够的。在这些情况下,我们可能需要就地修改对象或类。为此,Odoo 提供了实用功能patch。
覆盖/更新无法控制的其他一些组件/代码段的行为最有用。
二、Patch源码位置 :../web/static/src/core/utils/patch.js
引用方式: import { patch } from 'web.utils';
patch方法的定义:
/** @odoo-module **/ const patchMap = new WeakMap(); /** * Patch an object * * If the intent is to patch a class, don't forget to patch the prototype, unless * you want to patch static properties/methods. * 如果目的是修补一个类,不要忘记修补原型,除非你想修补静态属性方法。 * * @param {Object} obj 添加补丁的对象 * @param {string} patchName 补丁名 * @param {Object} patchValue 补丁值 * @param {{pure?: boolean}} [options] 其他选项 */ export function patch(obj, patchName, patchValue, options = {}) { const pure = Boolean(options.pure); if (!patchMap.has(obj)) { patchMap.set(obj, { original: {}, patches: [], }); } const objDesc = patchMap.get(obj); if (objDesc.patches.some((p) => p.name === patchName)) { throw new Error(`Class ${obj.name} already has a patch ${patchName}`); } objDesc.patches.push({ name: patchName, patch: patchValue, pure, }); for (const k in patchValue) { let prevDesc = null; let proto = obj; do { prevDesc = Object.getOwnPropertyDescriptor(proto, k); proto = Object.getPrototypeOf(proto); } while (!prevDesc && proto); const newDesc = Object.getOwnPropertyDescriptor(patchValue, k); if (!objDesc.original.hasOwnProperty(k)) { objDesc.original[k] = Object.getOwnPropertyDescriptor(obj, k); } if (prevDesc) { const patchedFnName = `${k} (patch ${patchName})`; if (prevDesc.value && typeof newDesc.value === "function") { makeIntermediateFunction("value", prevDesc, newDesc, patchedFnName); } if ((newDesc.get || newDesc.set) && (prevDesc.get || prevDesc.set)) { // get and set are defined together. If they are both defined // in the previous descriptor but only one in the new descriptor // then the other will be undefined so we need to apply the // previous descriptor in the new one. // get和set一起定义。如果它们都在前一个描述符中定义, // 但在新的描述符中只有一个,那么另一个将是未定义的,因此我们需要在新的描述符中应用前一个描述符。 // 发挥中间作用 newDesc.get = newDesc.get || prevDesc.get; newDesc.set = newDesc.set || prevDesc.set; if (prevDesc.get && typeof newDesc.get === "function") { makeIntermediateFunction("get", prevDesc, newDesc, patchedFnName); } if (prevDesc.set && typeof newDesc.set === "function") { makeIntermediateFunction("set", prevDesc, newDesc, patchedFnName); } } } Object.defineProperty(obj, k, newDesc); } function makeIntermediateFunction(key, prevDesc, newDesc, patchedFnName) { const _superFn = prevDesc[key]; const patchFn = newDesc[key]; if (pure) { newDesc[key] = patchFn; } else { newDesc[key] = { [patchedFnName](...args) { let prevSuper; if (this) { prevSuper = this._super; Object.defineProperty(this, "_super", { value: _superFn.bind(this), configurable: true, writable: true, }); } const result = patchFn.call(this, ...args); if (this) { Object.defineProperty(this, "_super", { value: prevSuper, configurable: true, writable: true, }); } return result; }, }[patchedFnName]; } } }
案例:设置主题的背景、前景、和图片方法,js部分代码截图
/** @odoo-module */ import { NavBar } from "@web/webclient/navbar/navbar"; import { registry } from "@web/core/registry"; const { fuzzyLookup } = require('@web/core/utils/search'); import { computeAppsAndMenuItems } from "@web/webclient/menus/menu_helpers"; import core from 'web.core'; const commandProviderRegistry = registry.category("command_provider"); import { patch } from 'web.utils'; var rpc = require('web.rpc'); //'backend_theme/static/src/components/app_menu/search_apps.js' // 字符串名称:官方写法是该js的路径来命名
//对象名称 NavBar.prototype
patch(NavBar.prototype, 'backend_theme/static/src/components/app_menu/search_apps.js', { //-------------------------------------------------------------------------- // Public 设置背景、前景、悬浮颜色 //-------------------------------------------------------------------------- /** * @override */ setup() { this._super(); this._search_def = $.Deferred(); let { apps, menuItems } = computeAppsAndMenuItems(this.menuService.getMenuAsTree("root")); this._apps = apps; this._searchableMenus = menuItems; this.colors = this.fetch_data(); }, fetch_data: function() { var self = this; rpc.query({model: 'res.config.settings',method: 'config_color_settings',args: [0],}).then(function(result){ self.colors = result; console.log("$$$",result); if (result.primary_accent !== false){ document.documentElement.style.setProperty("--primary-accent",result.primary_accent); } if (result.appbar_color !== false){ document.documentElement.style.setProperty("--app-bar-accent",result.appbar_color);} if (result.primary_hover !== false){ document.documentElement.style.setProperty("--primary-hover",result.primary_hover);} if (result.secondary_color !== false){ document.documentElement.style.setProperty("--primary-accent-border",result.secondary_color);} if (result.full_bg_img !== false){ document.documentElement.style.setProperty("--full-screen-bg",'url(data:image/png;base64,'+result.full_bg_img+')'); } if (result.appbar_text !== false){ document.documentElement.style.setProperty("--app-menu-font-color",result.appbar_text);} if (result.secoundary_hover !== false){ document.documentElement.style.setProperty("--secoundary-hover",result.secoundary_hover);} if (result.kanban_bg_color !== false){ document.documentElement.style.setProperty("--kanban-bg-color",result.kanban_bg_color);} }); }, });
心有猛虎,细嗅蔷薇