在Odoo 18的 QWeb报表中加载第三方库KaTeX时,Odoo未定义

前端开发 2026-07-09

我想在QWeb模板中,使用KaTeX的 JavaScript库把数学公式渲染成HTML。

我把KaTeX库本地下载下来并放到 __manifest__.py

'assets': {
    'web.report_assets_common': [
        'custom_quest/static/src/lib/katex/katex.css',
        'custom_quest/static/src/lib/katex/katex.js',
        'custom_quest/static/src/lib/katex/contrib/*.js',
    ],
}

在我的QWeb模板中,我输出包含LaTeX的 HTML字段,然后在页面底部执行一段脚本来渲染它:

    <template id="report_exam_paper_template">
        <!-- web.basic_layout provides proper HTML structure + UTF‑8 meta -->
        <t t-call="web.basic_layout">
            <t t-foreach="docs" t-as="exam">

                <div class="page" id="exam_question_container">

<!-- Report html code -->

<!-- After the code -->
 <script type="text/javascript">
                        <![CDATA[

                        console.log('Script block executed');

                        var container = document.getElementById('exam_question_container');
                        if (!container) {
                            alert('KaTeX Error: Container not found!');
                        }  else {
                            renderMathInElement(container, {
                                delimiters: [
                                    {left: '$$', right: '$$', display: true},
                                    {left: '\\[', right: '\\]', display: true},
                                    {left: '\\(', right: '\\)', display: false}
                                ],
                                throwOnError: false 
                            });
                        }
                        ]]>
                    </script>

                </div>

            </t>
        </t>
    </template>

请注意,脚本块确实会执行,所以并非无效。

问题描述: 在将PDF打印输出时,方程仍然以原始的纯文本形式显示。为了调试,我把报表类型切换到HTML,并用 ?debug=assets 在浏览器中加载。

在浏览器控制台中,执行会完全停滞,并出现以下两条错误:

  1. Uncaught ReferenceError: odoo is not defined(在 katex.js 的第1 行抛出)
  2. Uncaught ReferenceError: renderMathInElement is not defined(在我的QWeb脚本中抛出)

以下是katex.js的前几行:

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();
    else if(typeof define === 'function' && define.amd)
        define([], factory);
    else if(typeof exports === 'object')
        exports["katex"] = factory();
    else
        root["katex"] = factory();
})((typeof self !== 'undefined' ? self : this), function() {
return /******/ (function() { // webpackBootstrap
/******/    "use strict";
/******/    // The require scope
/******/    var __webpack_require__ = {};
/******/

有人能解释为什么会遇到这个错误以及如何解决吗?

如果你需要更多信息或上下文,请告诉我

解决方案

我不认为错误来自KaTeX本身,而是Odoo处理该文件的方式。

你把KaTeX放在:

custom_quest/static/src/lib/katex/katex.js

在Odoo 18中,位于 static/src 下的文件会被视为Odoo的前端源文件。Odoo可能会把它们转换成Odoo模块,这意味着提供的资源可能会被包装成如下形式:

odoo.define(...)

在普通的Odoo Web客户端中有效,因为 boot.js 定义了全局的 odoo 模块系统。但是一个QWeb报告并不是普通的Web客户端。它只是一个HTML报告页面,稍后由 wkhtmltopdf 渲染到PDF。因此,当报表加载你转换后的KaTeX文件时,找不到全局的 odoo,你会看到:

ReferenceError: odoo is not defined

然后,因为 katex.js 无法加载,自动渲染助手也永远不可用,因此你后面的脚本会失败,显示为:

ReferenceError: renderMathInElement is not defined

将第三方库从 static/src 移出,作为普通的浏览器库加载。例如:

custom_quest/static/lib/katex/katex.min.css
custom_quest/static/lib/katex/katex.min.js
custom_quest/static/lib/katex/contrib/auto-render.min.js

然后按顺序仅包含你实际需要的文件:

'assets': {
    'web.report_assets_common': [
        'custom_quest/static/lib/katex/katex.min.css',
        'custom_quest/static/lib/katex/katex.min.js',
        'custom_quest/static/lib/katex/contrib/auto-render.min.js',
    ],
}

不要使用:

'custom_quest/static/src/lib/katex/contrib/*.js'

用于这种情况。顺序很重要,renderMathInElement 是KaTeX的 auto-render 扩展专门提供的,而不是 contrib 中的每个文件都具备。

你的模板脚本在调用函数之前也应先确认该函数确实存在:

<script type="text/javascript">
    <![CDATA[
    (function () {
        var container = document.getElementById('exam_question_container');

        if (!container) {
            console.error('KaTeX: container not found');
            return;
        }

        if (typeof renderMathInElement !== 'function') {
            console.error('KaTeX auto-render was not loaded');
            return;
        }

        renderMathInElement(container, {
            delimiters: [
                {left: '$$', right: '$$', display: true},
                {left: '\\[', right: '\\]', display: true},
                {left: '\\(', right: '\\)', display: false}
            ],
            throwOnError: false
        });
    })();
    ]]>
</script>

或者,如果你必须把文件保留在 static/src 下,请把下面这行作为第三方JS文件的第一行添加:

/** @odoo-module ignore **/

但对于厂商库,较清晰的做法是把它们放在 static/lib,而不是 static/src

还有一个实用的警告:即使在 /report/html/... 也能工作,PDF输出仍然取决于 wkhtmltopdf 在渲染之前执行JavaScript。如果HTML报告渲染正确,但PDF仍显示原始LaTeX,那么资源加载问题已解决,剩下的问题是PDF的时序。在这种情况下,更稳妥的解决方案是在生成报表之前就服务器端渲染LaTeX,或者在报表引擎配置中强制足够的JavaScript延迟。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章