Odoo 18中<video>标签poster缩略图不显示,悬停懒加载播放逻辑失效问题排查
我帮你梳理下这个问题的可能原因和解决办法,你遇到的情况在Odoo里很常见,主要是几个细节没处理到位:
一、首先排查最容易忽略的错误:海报URL生成逻辑不对
你在静态HTML里用的海报URL是https://storage.googleapis.com/simstar-videos/50001593/still.jpg,对应的视频是https://storage.googleapis.com/simstar-videos/50001593/3D.mp4。但你Python代码里的替换逻辑是把.mp4换成/still.jpg,这样生成的海报URL会变成https://storage.googleapis.com/simstar-videos/50001593/3D/still.jpg——这和正确的路径完全不符,自然加载不出海报,显示空白!
修复后的海报URL生成逻辑:
我们需要把视频URL里的文件名去掉,再拼接still.jpg,而不是直接替换后缀:
from urllib.parse import urlparse, urlunparse def build_video_tag(url, label, width="180px", height="180px", extra_class=""): if url: # 解析URL结构,分离出路径部分 parsed_url = urlparse(url) path_segments = parsed_url.path.split('/') # 移除最后一段的视频文件名(比如3D.mp4) path_segments.pop() # 拼接海报路径 poster_path = '/'.join(path_segments) + '/still.jpg' poster_url = urlunparse(parsed_url._replace(path=poster_path)) # 同时把外层容器的class改成video-container,对应后面JS的选择器 return f""" <div class="video-container {extra_class}" style="position:relative; display:inline-block; margin:5px;"> <video data-src="{url}" poster="{poster_url}" muted playsinline preload="none" loop class="media-video" style="width: {width}; height: {height}; object-fit: cover; border: 1px solid #ccc; border-radius: 5px;"> </video> </div> """ return ""
二、Odoo的HTML安全过滤可能拦截了属性
Odoo默认会对渲染的HTML进行 sanitize 处理,可能会移除data-src这类自定义属性,或者某些内联样式,这会直接导致你的懒加载逻辑失效。
解决办法分两种场景:
- 如果是通过Python控制器直接返回HTML:
要在返回Response时关闭sanitize,避免属性被过滤:from odoo import http from odoo.http import make_response class MyDashboardController(http.Controller): @http.route('/my/custom/dashboard', type='http', auth='user', website=True) def get_dashboard(self): # 生成视频HTML video_html = self.build_video_tag("https://storage.googleapis.com/simstar-videos/50001593/3D.mp4", "Demo Video") # 用make_response返回,明确关闭sanitize return make_response(f"""<div class="dashboard">{video_html}</div>""", headers=[('Content-Type', 'text/html')], sanitize=False) - 如果是在QWeb模板中使用这个HTML:
需要在模板里添加t-options="{'sanitize': False}"来禁用过滤:<t t-esc="build_video_tag(url, label)" t-options="{'sanitize': False}"/>
三、JS事件绑定的时机和选择器不匹配
你原来的JS代码里用video.closest('.video-container')找外层容器,但Python生成的是video-wrapper类,导致找不到容器,后续的模糊、缩放逻辑报错,甚至可能影响视频播放。另外,直接用MutationObserver的初始化方式在Odoo里可能时机不对,应该用Odoo的组件体系来管理。
修复后的JS代码:
用Odoo的publicWidget来绑定事件,确保在页面加载完成后初始化,并且正确监听DOM变化:
odoo.define('my_module.video_lazy_loader', function (require) { "use strict"; var publicWidget = require('web.public.widget'); publicWidget.registry.videoLazyLoader = publicWidget.Widget.extend({ start: function () { this._bindAllVideos(); // 监听DOM新增元素,绑定新加载的视频 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.addedNodes.length) { this._bindAllVideos(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); return this._super.apply(this, arguments); }, _bindAllVideos: function () { // 只绑定未处理过的视频 document.querySelectorAll('video.media-video:not([data-bound="1"])').forEach(video => { video.dataset.bound = "1"; video.addEventListener('mouseenter', () => { const container = video.closest('.video-container'); const allContainers = document.querySelectorAll('.video-container'); // 加载并播放视频 if (!video.src && video.dataset.src) { video.src = video.dataset.src; video.load(); } video.play().catch(err => console.error("视频播放失败:", err)); // 调整当前视频样式 video.style.transform = 'scale(1.8)'; video.style.zIndex = '1000'; video.style.position = 'relative'; // 模糊其他视频容器 if (container) { allContainers.forEach(c => { if (c !== container) c.classList.add('blur'); }); } }); video.addEventListener('mouseleave', () => { const container = video.closest('.video-container'); const allContainers = document.querySelectorAll('.video-container'); // 重置视频状态 video.pause(); video.currentTime = 0; // 恢复样式 video.style.transform = 'scale(1)'; video.style.zIndex = ''; video.style.position = ''; // 移除其他容器的模糊效果 if (container) { allContainers.forEach(c => c.classList.remove('blur')); } }); }); } }); return publicWidget.registry.videoLazyLoader; });
四、别忘了加模糊效果的CSS
最后,在你的模块CSS文件里补充blur类的样式:
.video-container { transition: all 0.3s ease; } .video-container.blur { filter: blur(4px); opacity: 0.7; }
验证步骤
- 打开页面后,右键检查元素,确认video标签的
poster和data-src属性正确存在; - 打开浏览器控制台,查看是否有404错误(海报/视频)或JS报错;
- 测试鼠标hover,看视频是否正常加载播放,其他容器是否模糊。
内容来源于stack exchange

