mcpField
F
frontend-js

frontend-js

github.com/ahmed-lakosha开发7682 次安装31 周前更新
代码仓库

关于 frontend-js

面向 Odoo 14–19 前端 JavaScript 开发的参考技能,按版本提供网站主题、Owl 组件、翻译和 Bootstrap 迁移模式。

主要功能

  1. 01按 Odoo 版本提供指导
  2. 02使用 publicWidget 构建网站主题
  3. 03Owl 组件模式
  4. 04JavaScript 翻译实践
  5. 05Bootstrap 4 到 5 的迁移

适用场景

  1. 01实现 Odoo 网站主题交互
  2. 02按 Odoo 版本调整前端代码
  3. 03为 Odoo 16+ 迁移 Bootstrap 类名

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ahmed-lakosha/odoo-plugins --skill frontend-js

安装后在 Claude Code 中输入/frontend-js确认已加载。

详细介绍

Odoo 前端 JavaScript 模式

关键规则

  1. 网站主题:仅使用 publicWidget 框架,不要使用 Owl 或原生 JS
  2. JS 模块:每个文件都必须以 /** @odoo-module **/ 开头
  3. 禁止内联 JS/CSS:始终将文件分开放在 static/src/js/ 和 static/src/scss/ 中
  4. Bootstrap:Odoo 16 及更高版本使用 v5.1.3(绝不要使用 Tailwind)
  5. 翻译:静态 JS 标签必须在定义时使用 _t()

版本检测

OdooBootstrapOwlJavaScript
144.x—ES6+
154.xv1ES6+
165.1.3v1ES2020+
175.1.3v2ES2020+
18-195.1.3v2ES2020+

可根据路径(odoo17/ → v17)、manifest 版本字段或配置文件进行检测。


publicWidget 模式(主题必需)

用于:网站交互、主题功能、动画、表单

/** @odoo-module **/

import publicWidget from "@web/legacy/js/public/public_widget";

publicWidget.registry.MyWidget = publicWidget.Widget.extend({
    selector: '.my-selector',
    disabledInEditableMode: false,  // Allow in website builder

    events: {
        'click .button': '_onClick',
        'change input': '_onChange',
        'submit form': '_onSubmit',
    },

    /**
     * CRITICAL: Check editableMode for website builder compatibility
     */
    start: function () {
        if (!this.editableMode) {
            this._initializeAnimation();
            this._bindExternalEvents();
        }
        return this._super.apply(this, arguments);
    },

    _initializeAnimation: function () {
        this.$el.addClass('animated');
    },

    _bindExternalEvents: function () {
        $(window).on('scroll.myWidget', this._onScroll.bind(this));
        $(window).on('resize.myWidget', this._onResize.bind(this));
    },

    _onClick: function (ev) {
        ev.preventDefault();
        if (this.editableMode) return;
        // Handler logic
    },

    /**
     * CRITICAL: Clean up event listeners to prevent memory leaks
     */
    destroy: function () {
        $(window).off('.myWidget');  // Remove namespaced events
        this._super.apply(this, arguments);
    },
});

export default publicWidget.registry.MyWidget;
要点
  1. 在动画或交互前,务必检查 this.editableMode
  2. disabledInEditableMode: false 可使小部件在网站构建器中正常工作
  3. 务必在 destroy() 中清理事件监听器
  4. 网站主题绝不要使用 Owl 或原生 JS
  5. 使用带命名空间的事件(.myWidget),以便轻松清理
在清单中包含
'assets': {
    'web.assets_frontend': [
        'module_name/static/src/js/my_widget.js',
    ],
}

Owl 组件模式

Odoo 17(Owl v1)
/** @odoo-module **/

import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";

class MyComponent extends Component {
    setup() {
        this.state = useState({ items: [], loading: false });
    }

    async willStart() {
        await this.loadData();
    }
}

MyComponent.template = "module_name.MyComponentTemplate";
registry.category("public_components").add("MyComponent", MyComponent);
Odoo 18-19(Owl v2 — 重大变更)
/** @odoo-module **/

import { Component, useState } from "@odoo/owl";

class MyComponent extends Component {
    static template = "module_name.MyComponentTemplate";  // Static property
    static props = {
        title: { type: String, optional: true },
        items: { type: Array },
    };

    setup() {
        this.state = useState({ selectedId: null });
    }
}
XML 模板
<template id="MyComponentTemplate" name="My Component">
    <div class="my-component">
        <h3 t-if="props.title"><t t-esc="props.title"/></h3>
        <ul>
            <li t-foreach="props.items" t-as="item" t-key="item.id">
                <t t-esc="item.name"/>
            </li>
        </ul>
    </div>
</template>

翻译(_t)最佳实践

正确 — 在定义时包装
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";

// Static labels wrapped where defined
const MONTHS = [
    {value: 1, short: _t("Jan"), full: _t("January")},
    {value: 2, short: _t("Feb"), full: _t("February")},
    // ...
];

const STATUS_LABELS = {
    draft: _t("Draft"),
    pending: _t("Pending"),
    approved: _t("Approved"),
};
错误 — 运行时包装不起作用
// WRONG: Strings without _t() at definition
const MONTHS = [{value: 1, label: "Jan"}]; // NOT found by PO extractor!

// WRONG: Variable passed to _t() at runtime
translateLabel(key) {
    return _t(key);  // PO extractor can't find string literals
}
何时使用 _t()
使用 _t()不使用 _t()
JS 数组/对象中的静态标签XML 模板中的静态文本(自动翻译)
JS 常量中的错误消息运行时传入的动态变量
JS 中定义的面向用户的字符串.xml 文件中的硬编码字符串

Bootstrap 4 → 5 迁移(Odoo 14/15 → 16+)

类替换
Bootstrap 4Bootstrap 5
ml-*ms-*(起始侧外边距)
mr-*me-*(末尾侧外边距)
pl-*ps-*(起始侧内边距)
pr-*pe-*(末尾侧内边距)
text-lefttext-start
text-righttext-end
float-leftfloat-start
float-rightfloat-end
form-groupmb-3
custom-selectform-select
closebtn-close
badge-*bg-*
font-weight-boldfw-bold
sr-onlyvisually-hidden
no-guttersg-0
数据属性
Bootstrap 4Bootstrap 5
data-toggledata-bs-toggle
data-targetdata-bs-target
data-dismissdata-bs-dismiss
已移除的类(请查找替代项)
  • form-inline → 使用网格/弹性布局工具类
  • jumbotron → 使用工具类重新创建
  • media → 使用 d-flex 和弹性布局工具类

SCSS Bootstrap 覆盖

文件:static/src/scss/bootstrap_overridden.scss 资源包:web._assets_frontend_helpers

@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";

$spacer: 1rem !default;
$border-radius: 0.25rem !default;
$border-radius-lg: 0.5rem !default;
$box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !default;

所有覆盖项都使用 !default 标志。


特定版本说明

Odoo 17
  • Owl v1:将模板作为单独属性
  • 片段注册:使用简单 XPath
  • 导入:@web/legacy/js/public/public_widget
Odoo 18-19
  • Owl v2:静态模板、属性验证
  • 必须配置片段组
  • 网站构建器:插件架构(Odoo 19)
  • 重大变更:控制器中的 type='json' → type='jsonrpc'

评论

支持 Markdown