在Odoo 18的 QWeb报表中加载第三方库KaTeX时,Odoo未定义
我想在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 在浏览器中加载。
在浏览器控制台中,执行会完全停滞,并出现以下两条错误:
Uncaught ReferenceError: odoo is not defined(在katex.js的第1 行抛出)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延迟。