在Odoo QWeb电商模板中实现Isotope网格分页与筛选
Odoo QWeb集成Isotope实现产品网格筛选与分页
问题背景
我下载了一款Bootstrap电商模板,计划集成到Odoo QWeb模板系统中。当前产品网格视图的分页与筛选功能均未生效,需要借助Isotope网格完成筛选器(包含分类、子分类、品牌、价格区间)及产品网格的功能开发,现有产品网格代码如下:
<div t-attf-class="isotope-grid filter-grid cols-3 rows-1 mb-2" itemsPerPageDefault="6"> <div class="gutter-sizer"></div> <div class="grid-sizer"></div> <!-- Product--> <t t-foreach="Products" t-as="prods"> <div t-attf-class="grid-item scid{{prods.product_sub_cat_id.id}} cid{{prods.product_type_id.id}} brid{{prods.product_brand_id.id}}"> <div class="product-card"> <div class="product-badge text-danger"> <t t-esc="prods.discount" /> % Off </div> <a class="product-thumb" t-attf-href="/template/shop-single/{{slug(prods)}}"> <img t-att-src="image_data_uri(prods.first_image)" style="height: 200px;" alt="Product" /> </a> <h3 class="product-title"> <a t-attf-href="/template/shop-single/{{slug(prods)}}"> <t t-esc="prods.name" /> </a> </h3> <h4 class="product-price"> <del> pkr <t t-esc="prods.price" /> </del> pkr <t t-esc="prods.price - ((prods.discount*prods.price)/100)" /> </h4> <div class="product-buttons"> <button class="btn btn-outline-secondary btn-sm btn-wishlist" data-toggle="tooltip" title="Whishlist"> <i class="icon-heart"></i> </button> <form t-attf-id="fa{{prods.id}}" t-attf-action="/template/update-mycart" class="d-inline-block" method="GET"> <input type="hidden" t-attf-id="ia{{prods.id}}" t-att-name="product_id" t-attf-value="{{prods.id}}" title="" /> </form> <a t-attf-id="{{prods.id}}" onclick="addCart(this)" class="btn btn-outline-primary btn-sm" data-toast="" data-toast-type="success" data-toast-position="topRight" data-toast-icon="icon-circle-check" data-toast-title="Product" data-toast-message="successfuly added to cart!"> Add to Cart </a> </div> </div> </div> </t> </div>
实现步骤
1. 引入Isotope依赖
下载Isotope核心库及Isotope分页插件,将JS文件放入Odoo静态资源目录,在模板中引入:
<script src="/your-static-path/isotope.pkgd.min.js"></script> <script src="/your-static-path/isotope-pagination.min.js"></script>
2. 搭建筛选器HTML结构
在产品网格上方添加分类、子分类、品牌、价格区间筛选控件:
<!-- 筛选器容器 --> <div class="filter-controls mb-4"> <!-- 分类筛选 --> <div class="filter-group"> <label>分类:</label> <button class="filter-btn active" data-filter="*">全部</button> <t t-foreach="ProductCategories" t-as="cat"> <button class="filter-btn" t-att-data-filter=".cid{{cat.id}}"> <t t-esc="cat.name"/> </button> </t> </div> <!-- 子分类筛选 --> <div class="filter-group"> <label>子分类:</label> <button class="filter-btn active" data-filter="*">全部</button> <t t-foreach="ProductSubCategories" t-as="subcat"> <button class="filter-btn" t-att-data-filter=".scid{{subcat.id}}"> <t t-esc="subcat.name"/> </button> </t> </div> <!-- 品牌筛选 --> <div class="filter-group"> <label>品牌:</label> <button class="filter-btn active" data-filter="*">全部</button> <t t-foreach="ProductBrands" t-as="brand"> <button class="filter-btn" t-att-data-filter=".brid{{brand.id}}"> <t t-esc="brand.name"/> </button> </t> </div> <!-- 价格区间筛选 --> <div class="filter-group"> <label>价格区间:</label> <button class="filter-btn active" data-filter="*">全部</button> <button class="filter-btn" data-filter="[data-price-range='0-1000']">0-1000 PKR</button> <button class="filter-btn" data-filter="[data-price-range='1001-5000']">1001-5000 PKR</button> <button class="filter-btn" data-filter="[data-price-range='5001+']">5000+ PKR</button> </div> </div>
同时修改产品网格项,添加价格区间的data属性:
<div t-attf-class="grid-item scid{{prods.product_sub_cat_id.id}} cid{{prods.product_type_id.id}} brid{{prods.product_brand_id.id}}" t-att-data-price-range=" {{ '0-1000' if prods.price - ((prods.discount*prods.price)/100) <= 1000 else '1001-5000' if prods.price - ((prods.discount*prods.price)/100) <=5000 else '5001+' }}">
3. 初始化Isotope与分页功能
在模板底部添加JS脚本,完成筛选绑定和分页初始化:
<script> document.addEventListener('DOMContentLoaded', function() { // 初始化Isotope网格 const grid = document.querySelector('.isotope-grid'); const iso = new Isotope(grid, { itemSelector: '.grid-item', masonry: { columnWidth: '.grid-sizer', gutter: '.gutter-sizer' } }); // 绑定筛选按钮事件 document.querySelectorAll('.filter-btn').forEach(btn => { btn.addEventListener('click', function() { // 更新按钮激活状态 document.querySelectorAll('.filter-btn.active').forEach(el => el.classList.remove('active')); this.classList.add('active'); // 执行筛选 const filterValue = this.getAttribute('data-filter'); iso.arrange({ filter: filterValue }); // 筛选后重置分页 isoPagination.updateItems(); }); }); // 初始化分页组件 const isoPagination = new IsotopePagination(iso, { itemsPerPage: parseInt(grid.getAttribute('itemsPerPageDefault')) || 6, containerClass: 'isotope-pagination', itemClass: 'page-item', linkClass: 'page-link', activeClass: 'active', prevText: '上一页', nextText: '下一页', appendTo: grid.parentElement }); }); </script>
4. 适配Bootstrap样式(可选)
添加基础样式,让筛选器和分页组件适配Bootstrap风格:
.filter-group { display: inline-block; margin-right: 1.5rem; margin-bottom: 0.5rem; } .filter-btn { margin: 0 0.3rem; padding: 0.375rem 0.75rem; border-radius: 0.25rem; border: 1px solid #dee2e6; background-color: #fff; cursor: pointer; transition: all 0.2s; } .filter-btn.active { background-color: #0d6efd; color: #fff; border-color: #0d6efd; } .filter-btn:hover { background-color: #f8f9fa; } .isotope-pagination { margin-top: 2rem; display: flex; justify-content: center; list-style: none; padding-left: 0; } .page-item { margin: 0 0.25rem; } .page-link { padding: 0.5rem 0.75rem; border: 1px solid #dee2e6; border-radius: 0.25rem; text-decoration: none; color: #0d6efd; transition: background-color 0.2s; } .page-item.active .page-link { background-color: #0d6efd; color: #fff; border-color: #0d6efd; } .page-link:hover { background-color: #e9ecef; }
内容的提问来源于stack exchange,提问作者Ehsan Danish
相关产品推荐
相关产品推荐

