主题化
开发环境完全搭建好之后,你就可以开始构建主题模块的骨架了。在本章中,你将了解如何:
启用/禁用网站构建器的标准选项和模板。
定义设计中要使用的颜色和字体。
充分利用 Bootstrap 变量。
添加自定义样式和 JavaScript。
主题模块
Odoo 自带一个默认主题,它提供了最基本的结构和布局。创建新主题时,你实际上是在扩展这个默认主题。
请记得在开发环境中运行 Odoo 时,将包含你模块的目录添加到 addons-path 命令行参数中。
技术命名
第一步是创建一个新目录。
注解
在其前面加上 website_ 前缀,并且只使用小写 ASCII 字母数字字符和下划线。
在本文档中,我们将以 Airproof (一个虚构的项目)为例。
文件结构
主题的打包方式与任何 Odoo 模块相同。即使你只是在设计一个基础网站,也需要像打包模块那样打包它的主题。
website_airproof
├── data
├── i18n
├── static
│ ├── description
│ ├── fonts
│ ├── lib
│ ├── image_shapes // Shapes for images
│ ├── shapes // Shapes for background
│ └── src
│ ├── img
│ │ ├── content // For those used in the pages of your website
│ │ └── wbuilder // For those used in the builder
│ ├── js
│ ├── scss // Theme specific styles
│ ├── snippets // Custom snippets
│ └── website_builder // Options for the Website Builder
├── views
├── __init__.py
└── __manifest__.py
文件夹 |
说明 |
data |
预设、菜单、页面、图像、形状……(*.xml) |
i18n |
翻译(*.po、*.pot) |
lib |
外部库(*.js) |
static |
自定义资源(*.jpg、*.gif、*.png、*.svg、*.pdf、*.scss、*.js) |
views |
自定义视图和模板(*.xml) |
初始化
Odoo 模块同时也是一个 Python 包,其中包含一个 __init__.py 文件,该文件含有针对模块内各个 Python 文件的导入语句。目前这个文件可以保持为空。
声明
Odoo 模块由其清单文件来声明。该文件将一个 Python 包声明为 Odoo 模块,并指定模块的元数据。它至少必须包含 name 字段,该字段始终是必需的。它通常会包含更多信息。
/website_airproof/__manifest__.py
{
'name': 'Airproof Theme',
'description': '...',
'category': 'Website/Theme',
'version': '20.0.0',
'author': '...',
'license': '...',
'depends': ['website'],
'data': [
# ...
],
'assets': {
# ...
},
}
字段 |
说明 |
name |
模块的可读名称(必需) |
description |
模块的详细描述,使用 reStructuredText 编写 |
category |
在 Odoo 中的分类类别 |
version |
该模块所针对的 Odoo 版本 |
author |
模块作者名称 |
license |
默认情况下,我们使用 LGPL-3 许可证。更多信息请参阅 模块清单 页面。 |
depends |
必须先加载这些 Odoo 模块,才能加载本模块,原因可能是本模块使用了它们创建的功能,或者本模块修改了它们定义的资源 |
data |
XML 文件列表 |
assets |
SCSS 与 JS 文件列表 |
注解
上面的文件结构只是一个建议。项目中可以按需添加任意数量的其它文件夹,例如用 /controllers 存放控制器,或用 /views/backend 存放后端视图,等等。
要创建网站主题,只需安装 Website 应用。如需其它应用(博客、活动、电子商务等),也可以一并添加。
Odoo 版本号和主版本号是必填的,而补丁号是可选的。如果要指定运行模块所需的 Odoo 版本,应使用五段式结构,用前两段表示当前 Odoo 版本(* = 20.0)。
示例:20.0.1.0.0 odoo_major.odoo_minor.module_major.module_minor.module_patch
警告
使用通配符记法 (ex.: /myfolder/*.scss) 的自动文件包含在 Odoo SaaS 数据库中不起作用。这种情况下,请在清单文件中逐个手动包含每个文件。
默认选项
首先,尝试使用 Odoo 的默认选项来构建主题。这样可以确保两点:
你不会重复实现已经存在的内容。例如,Odoo 已经提供了为页脚添加边框的选项,就不要再自己重新编码。应先启用默认选项,需要时再对其进行扩展。
用户仍能在你的主题中使用 Odoo 的所有功能。例如,如果你重新编码页脚边框,可能会破坏默认选项或使其失效,给用户带来糟糕的体验。此外,其它 Odoo 功能可能依赖该默认选项,因此你的重写未必能像默认选项那样正常工作。
小技巧
每个缩进层级使用四个空格。
不要使用制表符。
切勿混用空格和制表符。
Odoo 变量
Odoo 声明了许多 CSS 规则,其中大部分都可以通过覆盖相关的 SCSS 变量来完全自定义。为此,请创建 primary_variables.scss 文件,并将其添加到 _assets_primary_variables 资源包中。
声明
/website_airproof/__manifest__.py
'assets': {
'web._assets_primary_variables': [
'website_airproof/static/src/scss/primary_variables.scss',
],
},
通过阅读源代码,可以很容易发现与选项相关的变量。
<BuilderSelectItem
id="'...'"
title.translate="..."
actionParam="[
{
action: 'websiteConfig',
actionParam: {
views: [...],
vars: { 'header-template': 'sidebar' },
checkVars: false,
}
}
]">
<Img src="'/website_airproof/static/src/img/wbuilder/template-header-opt.svg'" class="'mw-100'" />
</BuilderSelectItem>
例如,这些变量可以通过 $o-website-value-palettes 映射来覆盖。
全局
声明
/website_airproof/static/src/scss/primary_variables.scss
$o-website-values-palettes: (
(
// Templates
// Colors
// Fonts
// Buttons
// ...
),
);
小技巧
该文件只能包含 SCSS 变量和 Mixin 的定义与覆盖。
字体
你可以在网站上嵌入任意字体。网站构建器会自动让它们在字体选择器中可用。
声明
/website_airproof/static/src/scss/primary_variables.scss
$o-theme-font-configs: (
<font-name>: (
'family': <CSS font family list>,
'url' (optional): <related part of Google fonts URL>,
'properties' (optional): (
<font-alias>: (
<website-value-key>: <value>,
...,
),
...,
)
)
用法
/website_airproof/static/src/scss/primary_variables.scss
$o-website-values-palettes: (
(
'font': '<font-name>',
'headings-font': '<font-name>',
'navbar-font': '<font-name>',
'buttons-font': '<font-name>',
),
);
Google 字体
/website_airproof/static/src/scss/primary_variables.scss
$o-theme-font-configs: (
'Poppins': (
'family': ('Poppins', sans-serif),
'url': 'Poppins:400,500',
'properties' : (
'base': (
'font-size-base': 1rem,
),
),
),
);
自定义字体
首先,创建一个专门的 SCSS 文件来声明你的自定义字体。
/website_airproof/__manifest__.py
'assets': {
'web.assets_frontend': [
'website_airproof/static/src/scss/fonts.scss',
],
},
然后,使用 @font-face 规则让你的自定义字体可以在网站上加载。
/website_airproof/static/src/scss/fonts.scss
@font-face {
font-family: "My Custom Font", Helvetica, Helvetica Neue, Arial, sans-serif;
font-weight: 400;
font-style: normal;
src: url('/fonts/my-custom-font.woff') format('woff'),
url('/fonts/my-custom-font.woff2') format('woff2');
}
/website_airproof/static/src/scss/primary_variables.scss
$o-theme-font-configs: (
'Proxima Nova': (
'family': ('Proxima Nova', sans-serif),
'properties' : (
'base': (
'font-size-base': 1rem,
),
),
),
);
小技巧
建议为字体使用 .woff 和/或 .woff2 格式。
颜色
网站构建器依赖由五个具名颜色组成的调色板。在主题中定义这些颜色可以确保整体保持一致。
颜色 |
说明 |
o-color-1 |
主色 |
o-color-2 |
次色 |
o-color-3 |
附加(浅色) |
o-color-4 |
近白色 |
o-color-5 |
近黑色 |
声明
/website_airproof/static/src/scss/primary_variables.scss
$o-color-palettes: map-merge($o-color-palettes,
(
'airproof': (
'o-color-1': #bedb39,
'o-color-2': #2c3e50,
'o-color-3': #f2f2f2,
'o-color-4': #ffffff,
'o-color-5': #000000,
),
)
);
将创建的调色板添加到网站构建器提供的调色板列表中。
$o-selected-color-palettes-names: append($o-selected-color-palettes-names, 'airproof');
用法
/website_airproof/static/src/scss/primary_variables.scss
$o-website-values-palettes: (
(
'color-palettes-name': 'airproof',
),
);
颜色组合
基于前面定义的五个调色板颜色,网站构建器会自动生成五种颜色组合,每种组合分别为背景、文本、标题、链接、主要按钮和次要按钮定义一种颜色。用户之后可以自定义这些颜色。
颜色组合中使用的颜色可以被访问,并可通过 $o-color-palettes 使用特定前缀(o-cc,代表 color combination)来覆盖。
/website_airproof/static/src/scss/primary_variables.scss
$o-color-palettes: map-merge($o-color-palettes,
(
'airproof': (
'o-cc*-bg': 'o-color-*',
'o-cc*-text': 'o-color-*',
'o-cc*-headings': 'o-color-*',
'o-cc*-h2': 'o-color-*',
'o-cc*-h3': 'o-color-*',
'o-cc*-h4': 'o-color-*',
'o-cc*-h5': 'o-color-*',
'o-cc*-h6': 'o-color-*',
'o-cc*-link': 'o-color-*',
'o-cc*-btn-primary': 'o-color-*',
'o-cc*-btn-primary-border': 'o-color-*',
'o-cc*-btn-secondary': 'o-color-*',
'o-cc*-btn-secondary-border': 'o-color-*',
),
)
);
注解
对于每个 o-cc*,请把 * 替换为所需颜色组合对应的数字(1 - 5)。
默认文本颜色是 o-color-5。如果背景太暗,它会自动变为 o-color-4 颜色。
渐变
你也可以直接在 primary_variables.scss 文件中为菜单、页眉、页脚和版权栏定义渐变。
声明
/website_airproof/static/src/scss/primary_variables.scss
$o-website-values-palettes: (
(
'menu-gradient': linear-gradient(135deg, rgb(203, 94, 238) 0%, rgb(75, 225, 236) 100%),
'header-boxed-gradient': [your-gradient],
'footer-gradient': [your-gradient],
'copyright-gradient': [your-gradient],
),
);
Bootstrap 变量
Odoo 默认包含 Bootstrap。你可以使用该框架的所有变量和 Mixin。
如果 Odoo 没有提供你需要的变量,也许存在可以实现该需求的 Bootstrap 变量。实际上,所有 Odoo 布局都遵循 Bootstrap 结构,并使用 Bootstrap 组件或其扩展。若你自定义某个 Bootstrap 变量,就会为整个用户网站添加一种通用样式。
请使用添加到 _assets_frontend_helpers 资源包中的专用文件来覆盖 Bootstrap 值,而 不要 使用 primary_variables.scss 文件。
声明
/website_airproof/__manifest__.py
'assets': {
'web._assets_frontend_helpers': [
('prepend', 'website_airproof/static/src/scss/bootstrap_overridden.scss'),
],
},
用法
/website_airproof/static/src/scss/bootstrap_overridden.scss
// Typography
$h1-font-size: 4rem !default;
// Navbar
$navbar-nav-link-padding-x: 1rem!default;
// Buttons + Forms
$input-placeholder-color: o-color('o-color-1') !default;
// Cards
$card-border-width: 0 !default;
小技巧
该文件只能包含 SCSS 变量和 Mixin 的定义与覆盖。
警告
不要覆盖依赖 Odoo 变量的 Bootstrap 变量。否则,可能会破坏用户使用网站构建器自定义这些变量的能力。
当某个选项既在 primary_variables.scss 中由变量定义,又由 Bootstrap 变量定义时,应始终通过主要变量来覆盖。只有当主要变量中不存在相应内容时,才通过 bootstrap_overridden.scss 覆盖。
字号
Odoo 提供了 CSS 字号类,用于分离样式(字号)与语义(标签及一般样式)。两种逻辑可以结合使用,从而更加灵活。
文本样式
Odoo 的网站构建器允许为文本选择样式。有些样式只与标签相关,例如不带任何额外 CSS 类的 Header;另一些则把标签与直接应用在其上的样式结合起来,例如 Header 1 Display。
<!-- h1 with display heading sizes -->
<h1 class="display-1">Heading 1 with Display Heading 1 size</h1>
<h1 class="display-2">Heading 1 with Display Heading 2 size</h1>
<h1 class="display-3">Heading 1 with Display Heading 3 size</h1>
<h1 class="display-4">Heading 1 with Display Heading 4 size</h1>
<!-- Lead text - named "Light" in the dropdown -->
<p class="lead">A text typically used as an introduction.</p>
<!-- Small text -->
<p class="o_small">Body text with a smaller size.</p>
尺寸类
尺寸类会添加在目标元素内部新建的 span 标签上(见下面的示例)。
标题与正文文本
假设这些类可以应用于任何文本元素,下面以 h2 为例:
<!-- h2 sized like an h1 -->
<h2><span class="h1-fs">Heading</span></h2>
<!-- h2 sized with other heading sizes -->
<h2><span class="h2-fs">Heading</span></h2>
<h2><span class="h3-fs">Heading</span></h2>
<h2><span class="h4-fs">Heading</span></h2>
<h2><span class="h5-fs">Heading</span></h2>
<h2><span class="h6-fs">Heading</span></h2>
<!-- h2 sized like a normal paragraph (base size, 16px by default) -->
<h2><span class="base-fs">Heading</span></h2>
<!-- h2 sized like a small text (14px by default) -->
<h2><span class="o_small-fs">Heading</span></h2>
显示标题
如果需要更大的标题,Odoo 会使用 Bootstrap 的显示标题(Display Headings)类,从 display-1 到 6。
<h2><span class="display-1-fs">Heading</span></h2>
<h2><span class="display-2-fs">Heading</span></h2>
<h2><span class="display-3-fs">Heading</span></h2>
<h2><span class="display-4-fs">Heading</span></h2>
注解
网站构建器只允许用户配置 Display 1 到 Display 4 的尺寸。你可以在代码中设置其它尺寸(5 和 6)并使用,但用户无法直接在网站构建器界面中修改它们。
网站设置
与网站相关的全局选项可以按照下面的结构,通过网站记录进行设置。
声明
/website_airproof/data/website.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="base.default_website" model="website">
<field name="name">Airproof</field>
<field name="logo" type="bytes" file="website_airproof/static/src/img/content/logo_airproof.png"/>
<field name="favicon" type="bytes" file="website_airproof/static/description/favicon.png" />
<field name="shop_ppg">18</field>
<field name="shop_ppr">3</field>
<field name="shop_gap">16px</field>
<field name="shop_opt_products_design_classes">
o_wsale_products_opt_layout_catalog
o_wsale_products_opt_design_cards
o_wsale_products_opt_name_color_regular
o_wsale_products_opt_thumb_cover
o_wsale_products_opt_img_hover_zoom_out_light
o_wsale_products_opt_has_cta
o_wsale_products_opt_actions_onhover
o_wsale_products_opt_wishlist_fixed
o_wsale_products_opt_cc
o_wsale_products_opt_cc1
o_wsale_products_opt_rounded_2
o_wsale_products_opt_actions_promote
o_wsale_products_opt_actions_subtle
o_wsale_products_opt_name_weight_bold
</field>
<field name="cookies_bar" eval="True" />
<field name="contact_us_link_url">/contact-us</field>
<field name="social_facebook">https://www.facebook.com/Airproof</field>
<field name="social_instagram">https://www.instagram.com/airproof</field>
<field name="social_linkedin">https://www.linkedin.com/company/airproof</field>
<field name="social_youtube">https://www.youtube.com/c/airproof</field>
</record>
</odoo>
更多内容
来自电子商务应用的 网站模型:
以 shop_ 为前缀的字段与 /shop 页面配置相关;
以 product_page_ 为前缀的字段与产品页面配置相关;
“卡片设计” 类由 /shop 页面和动态产品片段共享。
字段 |
说明 |
name |
网站名称(在浏览器中显示) |
logo |
徽标路径(此前已在记录中创建) |
favicon |
网站图标路径(此前已在记录中创建) |
shop_ppg(每页产品数) |
电子商务中每页显示的产品数量 |
shop_ppr(每行产品数) |
电子商务中每行(每页内)显示的产品数量 |
shop_gap(网格间距) |
用于分隔产品网格各项的间距值 |
shop_opt_products_design_classes(产品设计类列表) |
与产品设计选项相关的 CSS 类列表。 |
cookies_bar(Cookie 栏) |
启用/禁用 Cookie 栏 |
contact_us_link_url(联系页面 URL) |
联系我们 页面的 URL(例如用于标准页眉模板中)。
|
social_facebook(Facebook 主页) |
Facebook 个人主页的 URL |
social_instagram(Instagram 主页) |
Instagram 个人主页的 URL |
social_linkedin(LinkedIn 主页) |
LinkedIn 公司主页的 URL |
social_youtube(YouTube 频道) |
YouTube 频道的 URL |
注解
base.default_website 是你只使用一个网站时的默认引用。如果数据库中有多个网站,该记录将指向默认站点(即第一个网站)。
视图
对于某些选项,除了网站构建器变量之外,还需要激活特定的视图。
通过阅读源代码,可以很容易找到与选项相关的模板。
<BuilderContext action="'websiteConfig'">
<BuilderRow label.translate="...">
<BuilderCheckbox actionParam="{views: ['website.show_website_info']}" />
</BuilderRow>
</BuilderContext>
<template id="..." inherit_id="..." name="..." active="True"/>
<template id="..." inherit_id="..." name="..." active="False"/>
更多内容
在这种情况下,在 XML 中定义记录时应优先使用 <template id="..."> 语法,而不是 <record id="..." model="ir.ui.view"> 语法。
请参阅 template,尤其是关于 active 值的内容。
预设组合
为了把视图作为预设启用或停用,应将这些视图包含在 presets.xml 文件中。
用法
/website_airproof/data/presets.xml
<record id="module.view" model="ir.ui.view">
<field name="active" eval="False"/>
</record>
示例
更改菜单项的水平对齐方式
/website_airproof/data/presets.xml
<record id="website.template_header_default_align_center" model="ir.ui.view">
<field name="active" eval="True"/>
</record>
同样的逻辑也适用于其它 Odoo 应用。
电子商务 - 显示产品分类
/website_airproof/data/presets.xml
<record id="website_sale.products_categories" model="ir.ui.view">
<field name="active" eval="False"/>
</record>
门户 - 禁用语言选择器
/website_airproof/data/presets.xml
<record id="portal.footer_language_selector" model="ir.ui.view">
<field name="active" eval="False"/>
</record>
资产
这部分我们将参考 web 模块中的 assets_frontend 资源包。该资源包指定了网站前端(公开页面)加载的资源列表,目标是将你的 SCSS 和 JS 文件添加到该资源包中。网站构建器使用它自己的资源包(例如 website.website_builder_assets 和 website.assets_wysiwyg)。
以下是网站常用资源包的非详尽列表:
资源包 |
说明 |
web._assets_primary_variables 资源包
|
定义主要的 SCSS 变量(颜色、字体、间距等),作为所有资源包共用的主要主题层。主要用于 primary_variables.scss 文件。 |
web._assets_secondary_variables 资源包
|
定义依赖并扩展主要变量的次要 SCSS 变量。主要用于 secondary_variables.scss 文件。 |
web._assets_frontend_helpers 资源包
|
提供前端专用的 Bootstrap 覆盖,需在任何前端 SCSS 编译之前引入。主要用于 bootstrap_overridden.scss 文件。 |
web.assets_frontend 资源包
|
提供渲染 Odoo 前端并与其交互所需的基础资源。你可以在此添加所有自定义的 SCSS、JS 或客户端 QWeb 模板。 |
website.website_builder_assets 资源包
|
提供加载到网站构建器上下文中的资源,包括编辑模式样式、为自定义构建块提供的自定义方法等。 |
web._assets_bootstrap 资源包 |
例如,如果需要通过 Bootstrap Utilities API 扩展 Bootstrap |
样式
网站构建器与 Bootstrap 非常适合定义网站的基础样式。但要设计出独特的效果,你需要更进一步。为此,你可以轻松地将任意 SCSS 文件添加到主题中。
声明
/website_airproof/__manifest__.py
'assets': {
'web.assets_frontend': [
'website_airproof/static/src/scss/theme.scss',
],
},
你可以随意复用 Bootstrap 文件中的变量,以及 Odoo 在 theme.scss 文件中使用的变量。
示例
/website_airproof/static/src/scss/theme.scss
blockquote {
border-radius: $rounded-pill;
color: o-color('o-color-3');
font-family: o-website-value('headings-font');
}
交互性
Odoo 支持三种不同的 JavaScript 文件:
大多数新的 Odoo JavaScript 代码应使用原生 JavaScript 模块系统。它更简单,并且能带来更好的开发者体验,与 IDE 的集成也更好。
声明
/website_airproof/__manifest__.py
'assets': {
'web.assets_frontend': [
'website_airproof/static/src/js/theme.js',
],
},
注解
如果要引入外部库中的文件,可以将它们添加到模块的 /lib 文件夹中。
小技巧
使用 linter(ESLint 等)。
始终使用压缩过的 JavaScript 库
对要用 JavaScript 操作的元素使用以 js_ 为前缀的 CSS 类。
变量和函数应使用 camelCased (myVariable)而不是 snake_cased (my_variable)。
JavaScript 类使用 PascalCase (MyClass)。
不要将变量命名为 event,而应使用 ev。这样可以避免在非 Chrome 浏览器上出现缺陷,因为 Chrome 会神奇地分配一个全局 event 变量(因此,如果在未声明的情况下使用 event 变量,它在 Chrome 上能正常工作,但在其他所有浏览器上都会崩溃)。
使用严格比较(=== 而不是 ==)。
所有字符串都使用双引号(例如 "Hello"、".x_nav_item")。
如果使用原生标准 JS 函数(start()、willStart()、cleanForSave() 等),请确保调用父类实现(例如 super.start(...arguments);并检查在标准代码中是否有此必要)。