一、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);}
});
},
});
心有猛虎,细嗅蔷薇