You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Odoo15中实现支持前端添图的动态Banner图片轮播?

Odoo15 动态图片轮播组件实现方案

一、创建后端数据模型

首先搭建存储轮播图片的模型,包含图片、标题、跳转链接等核心字段,支持排序与状态控制:

from odoo import models, fields, api

class SliderImage(models.Model):
    _name = 'slider.image'
    _description = '轮播图片'
    _order = 'sequence asc, create_date desc'

    name = fields.Char('标题', required=True)
    image = fields.Binary('轮播图片', required=True)
    image_url = fields.Char('图片链接', compute='_compute_image_url')
    link = fields.Char('跳转链接')
    sequence = fields.Integer('排序', default=10)
    active = fields.Boolean('是否激活', default=True)

    @api.depends('image')
    def _compute_image_url(self):
        for record in self:
            if record.image:
                record.image_url = f'/web/image/slider.image/{record.id}/image'
            else:
                record.image_url = ''

二、配置权限与菜单

为普通用户开放轮播图片的增删改查权限,并添加后台管理入口:

  1. 在security/ir.model.access.csv中添加权限配置:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_slider_image_user,slider.image.user,model_slider_image,base.group_user,1,1,1,1
  1. 在views/menu.xml中添加后台菜单:
<odoo>
    <menuitem name="轮播图片管理" id="menu_slider_image" parent="base.menu_marketing" action="action_slider_image"/>
    <record id="action_slider_image" model="ir.actions.act_window">
        <field name="name">轮播图片</field>
        <field name="res_model">slider.image</field>
        <field name="view_mode">tree,form</field>
        <field name="target">current</field>
    </record>
</odoo>

三、前端轮播组件实现

1. QWeb模板(轮播展示+上传表单)

创建views/templates.xml,编写动态渲染的轮播结构与用户上传表单:

<odoo>
    <template id="slider_template" name="动态图片轮播">
        <t t-call="website.layout">
            <div class="slider-container">
                <div class="slideshow-container">
                    <t t-foreach="slider_images" t-as="slide">
                        <div class="mySlides fade">
                            <div class="numbertext"><t t-esc="slide_index + 1"/> / <t t-esc="total_slides"/></div>
                            <img t-att-src="slide.image_url" alt="轮播图片" class="slider-image"/>
                            <div class="text"><t t-esc="slide.name"/></div>
                        </div>
                    </t>

                    <!-- 切换按钮 -->
                    <a class="prev" onclick="plusSlides(-1)">&#10094;</a>
                    <a class="next" onclick="plusSlides(1)">&#10095;</a>
                </div>
                <br>

                <!-- 指示器 -->
                <div style="text-align:center">
                    <t t-foreach="slider_images" t-as="slide">
                        <span class="dot" t-att-data-index="slide_index" onclick="currentSlide(slide_index + 1)"></span>
                    </t>
                </div>

                <!-- 用户上传表单 -->
                <div class="slider-upload-form">
                    <h3>添加轮播图片</h3>
                    <form id="slider-upload-form">
                        <div class="form-group">
                            <label>标题</label>
                            <input type="text" name="name" required/>
                        </div>
                        <div class="form-group">
                            <label>跳转链接(可选)</label>
                            <input type="url" name="link"/>
                        </div>
                        <div class="form-group">
                            <label>排序</label>
                            <input type="number" name="sequence" value="10"/>
                        </div>
                        <div class="form-group">
                            <label>选择图片</label>
                            <input type="file" name="image" accept="image/*" required/>
                        </div>
                        <button type="submit">提交</button>
                    </form>
                </div>
            </div>
        </t>
    </template>
</odoo>

2. CSS样式

创建static/src/css/slider.css,定义轮播与表单的基础样式:

.slideshow-container {
  max-width: 1000px;
  position: relative;
  margin: auto;
}

.mySlides {
  display: none;
}

.slider-image {
  width: 100%;
  height: 400px;
  object-fit: cover;
}

.prev, .next {
  cursor: pointer;
  position: absolute;
  top: 50%;
  width: auto;
  margin-top: -22px;
  padding: 16px;
  color: white;
  font-weight: bold;
  font-size: 18px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
  user-select: none;
}

.next {
  right: 0;
  border-radius: 3px 0 0 3px;
}

.prev:hover, .next:hover {
  background-color: rgba(0,0,0,0.8);
}

.text {
  color: #f2f2f2;
  font-size: 15px;
  padding: 8px 12px;
  position: absolute;
  bottom: 8px;
  width: 100%;
  text-align: center;
}

.numbertext {
  color: #f2f2f2;
  font-size: 12px;
  padding: 8px 12px;
  position: absolute;
  top: 0;
}

.dot {
  cursor: pointer;
  height: 15px;
  width: 15px;
  margin: 0 2px;
  background-color: #bbb;
  border-radius: 50%;
  display: inline-block;
  transition: background-color 0.6s ease;
}

.active, .dot:hover {
  background-color: #717171;
}

.fade {
  animation-name: fade;
  animation-duration: 1.5s;
}

@keyframes fade {
  from {opacity: .4}
  to {opacity: 1}
}

.slider-upload-form {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.form-group {
  margin-bottom: 15px;
}

.form-group label {
  display: block;
  margin-bottom: 5px;
}

.form-group input {
  width: 100%;
  padding: 8px;
  box-sizing: border-box;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.slider-upload-form button {
  background-color: #4CAF50;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.slider-upload-form button:hover {
  background-color: #45a049;
}

3. JavaScript逻辑

创建static/src/js/slider.js,实现轮播交互与图片上传功能:

odoo.define('website.slider', function (require) {
    'use strict';

    var ajax = require('web.ajax');
    var core = require('web.core');
    var _t = core._t;

    let slideIndex = 1;
    let sliderImages = [];

    // 初始化轮播数据
    function initSlider() {
        ajax.jsonRpc('/web/search_read', 'call', {
            model: 'slider.image',
            fields: ['name', 'image_url', 'link', 'sequence'],
            domain: [['active', '=', true]]
        }).then(function (data) {
            sliderImages = data.records;
            // 渲染轮播元素
            renderSliderElements();
            showSlides(slideIndex);
            // 自动轮播
            setInterval(() => plusSlides(1), 5000);
        });
    }

    // 渲染轮播元素到页面
    function renderSliderElements() {
        const slidesContainer = document.querySelector('.slideshow-container');
        const dotsContainer = document.querySelector('.slideshow-container + br + div');
        
        // 清空原有内容
        slidesContainer.innerHTML = '';
        dotsContainer.innerHTML = '';

        // 生成轮播项
        sliderImages.forEach((slide, index) => {
            const slideDiv = document.createElement('div');
            slideDiv.className = 'mySlides fade';
            slideDiv.innerHTML = `
                <div class="numbertext">${index + 1} / ${sliderImages.length}</div>
                <img src="${slide.image_url}" alt="轮播图片" class="slider-image"/>
                <div class="text">${slide.name}</div>
            `;
            slidesContainer.appendChild(slideDiv);

            // 生成指示器
            const dotSpan = document.createElement('span');
            dotSpan.className = 'dot';
            dotSpan.dataset.index = index;
            dotSpan.onclick = () => currentSlide(index + 1);
            dotsContainer.appendChild(dotSpan);
        });

        // 添加切换按钮
        const prevBtn = document.createElement('a');
        prevBtn.className = 'prev';
        prevBtn.innerHTML = '&#10094;';
        prevBtn.onclick = () => plusSlides(-1);
        slidesContainer.appendChild(prevBtn);

        const nextBtn = document.createElement('a');
        nextBtn.className = 'next';
        nextBtn.innerHTML = '&#10095;';
        nextBtn.onclick = () => plusSlides(1);
        slidesContainer.appendChild(nextBtn);
    }

    // 切换轮播图
    function plusSlides(n) {
        showSlides(slideIndex += n);
    }

    // 跳转到指定轮播图
    function currentSlide(n) {
        showSlides(slideIndex = n);
    }

    // 显示指定轮播图
    function showSlides(n) {
        const slides = document.getElementsByClassName("mySlides");
        const dots = document.getElementsByClassName("dot");
        if (n > slides.length) slideIndex = 1;
        if (n < 1) slideIndex = slides.length;
        
        Array.from(slides).forEach(slide => slide.style.display = "none");
        Array.from(dots).forEach(dot => dot.className = dot.className.replace(" active", ""));
        
        slides[slideIndex-1].style.display = "block";
        dots[slideIndex-1].className += " active";
    }

    // 图片上传处理
    document.addEventListener('DOMContentLoaded', function() {
        initSlider();

        const form = document.getElementById('slider-upload-form');
        if (form) {
            form.addEventListener('submit', function(e) {
                e.preventDefault();

                const formData = new FormData(form);
                const name = formData.get('name');
                const link = formData.get('link');
                const sequence = formData.get('sequence');
                const imageFile = formData.get('image');

                // 图片转Base64
                const reader = new FileReader();
                reader.onload = function(event) {
                    const imageBase64 = event.target.result.split(',')[1];

                    // 提交到后端
                    ajax.jsonRpc('/web/dataset/call_kw', 'call', {
                        model: 'slider.image',
                        method: 'create',
                        args: [{
                            'name': name,
                            'link': link,
                            'sequence': parseInt(sequence),
                            'image': imageBase64
                        }],
                        kwargs: {}
                    }).then(function() {
                        alert(_t('图片添加成功!'));
                        form.reset();
                        initSlider();
                    }).catch(function(error) {
                        alert(_t('图片添加失败:') + error.message);
                    });
                };
                reader.readAsDataURL(imageFile);
            });
        }
    });

    // 暴露全局函数
    window.plusSlides = plusSlides;
    window.currentSlide = currentSlide;

    return {
        initSlider: initSlider
    };
});

4. 资源加载配置

在views/assets.xml中注册CSS与JS资源:

<odoo>
    <template id="assets_frontend" inherit_id="website.assets_frontend">
        <xpath expr="." position="inside">
            <link rel="stylesheet" href="/your_module_name/static/src/css/slider.css"/>
            <script type="text/javascript" src="/your_module_name/static/src/js/slider.js"></script>
        </xpath>
    </template>
</odoo>

替换your_module_name为你的模块名称。

四、页面集成

将轮播组件添加到网站首页(或其他目标页面):

<odoo>
    <template id="website_homepage" inherit_id="website.homepage">
        <xpath expr="//div[hasclass('o_homepage')]" position="inside">
            <t t-call="your_module_name.slider_template"/>
        </xpath>
    </template>
</odoo>

五、功能说明

  • 动态轮播:从后端拉取激活状态的图片,支持自定义排序与5秒自动切换
  • 用户上传:前端表单支持上传图片、填写标题与链接,提交后自动同步到后端并刷新轮播
  • 交互控制:支持前后按钮切换、指示器跳转,hover效果增强用户体验
  • 样式适配:基础样式适配通用场景,可根据需求修改CSS调整展示效果

内容的提问来源于stack exchange,提问作者Nirmala Sudhir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:05:27