[IMP] product, *: add image option for color attribute
*: website_sale, sale_product_configurator With this commit, Image options for the color variant have been implemented. Now, users can use images to represent pattern colors such as "leopard" instead of solely selecting a solid color. - User can add, remove or change the color attribute value image from the backend. If no image has been uploaded, it will use the corresponding HEX color to represent color/patterns. task-3381738 Part-of: odoo/odoo#132685
This commit is contained in:
committed by
Antoine Vandevenne (anv)
parent
c13332a186
commit
c05336d273
@@ -44,6 +44,12 @@ class ProductAttributeValue(models.Model):
|
||||
" to display the color if the attribute type is 'Color'.")
|
||||
display_type = fields.Selection(related='attribute_id.display_type')
|
||||
color = fields.Integer(string="Color Index", default=_get_default_color)
|
||||
image = fields.Image(
|
||||
string="Image",
|
||||
help="You can upload an image that will be used as the color of the attribute value.",
|
||||
max_width=70,
|
||||
max_height=70,
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
('value_company_uniq',
|
||||
|
||||
@@ -60,6 +60,7 @@ class ProductTemplateAttributeValue(models.Model):
|
||||
is_custom = fields.Boolean(related='product_attribute_value_id.is_custom')
|
||||
display_type = fields.Selection(related='product_attribute_value_id.display_type')
|
||||
color = fields.Integer(string="Color", default=_get_default_color)
|
||||
image = fields.Image(related='product_attribute_value_id.image')
|
||||
|
||||
_sql_constraints = [
|
||||
('attribute_value_unique',
|
||||
|
||||
@@ -46,7 +46,12 @@
|
||||
<field name="name"/>
|
||||
<field name="display_type" column_invisible="True"/>
|
||||
<field name="is_custom" groups="product.group_product_variant" column_invisible="parent.display_type == 'multi'"/>
|
||||
<field name="html_color" column_invisible="parent.display_type != 'color'" widget="color"/>
|
||||
<field name="html_color" column_invisible="parent.display_type != 'color'" invisible="image" widget="color"/>
|
||||
<field name="image"
|
||||
class="oe_avatar text-start float-none"
|
||||
column_invisible="parent.display_type != 'color'"
|
||||
options="{'size': [70, 70]}"
|
||||
widget="image"/>
|
||||
<field name="default_extra_price"/>
|
||||
</tree>
|
||||
</field>
|
||||
@@ -95,7 +100,8 @@
|
||||
<field name="attribute_id" optional="hide"/>
|
||||
<field name="name"/>
|
||||
<field name="display_type" optional="hide"/>
|
||||
<field name="html_color" invisible="display_type != 'color'" widget="color"/>
|
||||
<field name="html_color" invisible="display_type != 'color' or image" widget="color"/>
|
||||
<field name="image" invisible="display_type != 'color'" widget="image" options="{'size': [70, 70]}"/>
|
||||
<field name="ptav_active" optional="hide"/>
|
||||
<field name="price_extra" widget="monetary" options="{'field_digits': True}"/>
|
||||
<field name="currency_id" column_invisible="True"/>
|
||||
@@ -113,7 +119,8 @@
|
||||
<field name="ptav_active" readonly="1" invisible="ptav_active"/>
|
||||
<field name="name"/>
|
||||
<field name="display_type" invisible="1"/>
|
||||
<field name="html_color" invisible="display_type != 'color'"/>
|
||||
<field name="html_color" invisible="display_type != 'color' or image"/>
|
||||
<field name="image" invisible="display_type != 'color' or not image" widget="image" options="{'size': [70, 70]}"/>
|
||||
<field name="price_extra" widget="monetary" options="{'field_digits': True}"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="exclude_for" widget="one2many" mode="tree">
|
||||
|
||||
@@ -236,6 +236,7 @@ class ProductConfiguratorController(Controller):
|
||||
'name': str,
|
||||
'price_extra': float,
|
||||
'html_color': str|False,
|
||||
'image': str|False,
|
||||
'is_custom': bool
|
||||
}],
|
||||
'selected_attribute_id': int,
|
||||
@@ -270,7 +271,7 @@ class ProductConfiguratorController(Controller):
|
||||
attribute=dict(**ptal.attribute_id.read(['id', 'name', 'display_type'])[0]),
|
||||
attribute_values=[
|
||||
dict(
|
||||
**ptav.read(['name', 'html_color', 'is_custom'])[0],
|
||||
**ptav.read(['name', 'html_color', 'image', 'is_custom'])[0],
|
||||
price_extra=ptav.currency_id._convert(
|
||||
ptav.price_extra,
|
||||
currency,
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ export class ProductTemplateAttributeLine extends Component {
|
||||
id: Number,
|
||||
name: String,
|
||||
html_color: [Boolean, String], // backend sends 'false' when there is no color
|
||||
image: [Boolean, String], // backend sends 'false' when there is no image set
|
||||
is_custom: Boolean,
|
||||
price_extra: Number,
|
||||
excluded: { type: Boolean, optional: true },
|
||||
|
||||
+3
-1
@@ -92,9 +92,11 @@
|
||||
<ul class="list-inline">
|
||||
<li t-foreach="this.props.attribute_values" t-as="ptav" t-key="ptav.id"
|
||||
class="list-inline-item me-1">
|
||||
<t t-set="img_style" t-value="ptav.image ? 'background:url(/web/image/product.template.attribute.value/'+ptav.id+'/image); background-size:cover;' : ''"/>
|
||||
<t t-set="color_style" t-value="ptav.is_custom ? '' : 'background-color:' + ptav.html_color"/>
|
||||
<label
|
||||
t-att-title="ptav.name"
|
||||
t-attf-style="background-color:#{ptav.is_custom ? '' : ptav.html_color}"
|
||||
t-attf-style="#{img_style or color_style}"
|
||||
t-att-class="{'o_sale_product_configurator_ptav_color': true,
|
||||
'active': this.props.selected_attribute_value_ids.includes(ptav.id),
|
||||
'custom_value': ptav.is_custom,
|
||||
|
||||
@@ -493,6 +493,27 @@
|
||||
<template id="website_sale.products_categories_top" active="True" name="Categories in top-nav"/>
|
||||
<template id="website_sale.products_attributes_top" active="False" name="Attributes in top-nav"/>
|
||||
|
||||
<template id="o_wsale_offcanvas_color_attribute" name="Color type attribute in filter">
|
||||
<t t-foreach="a.value_ids" t-as="v">
|
||||
<t t-set="img_style"
|
||||
t-value="'background:url(/web/image/product.attribute.value/%s/image); background-size:cover;' % v.id if v.image else ''"
|
||||
/>
|
||||
<t t-set="color_style"
|
||||
t-value="'background: ' + str(v.html_color or v.name if not v.is_custom else '')"
|
||||
/>
|
||||
<label t-attf-style="#{img_style or color_style}"
|
||||
t-attf-class="css_attribute_color mb-1 #{'active' if v.id in attrib_set else ''}"
|
||||
>
|
||||
<input type="checkbox"
|
||||
name="attrib"
|
||||
t-att-value="'%s-%s' % (a.id, v.id)"
|
||||
t-att-checked="'checked' if v.id in attrib_set else None"
|
||||
t-att-title="v.name"
|
||||
/>
|
||||
</label>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- OffCanvas Nav -->
|
||||
<template id="website_sale.o_wsale_offcanvas" name="Offcanvas">
|
||||
<aside id="o_wsale_offcanvas"
|
||||
@@ -645,11 +666,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="a.display_type == 'color'" class="pt-1 pb-3">
|
||||
<t t-foreach="a.value_ids" t-as="v">
|
||||
<label t-attf-style="background-color:#{v.html_color or v.name}" t-attf-class="css_attribute_color mb-1 #{'active' if v.id in attrib_set else ''}">
|
||||
<input type="checkbox" name="attrib" t-att-value="'%s-%s' % (a.id,v.id)" t-att-checked="'checked' if v.id in attrib_set else None" t-att-title="v.name" />
|
||||
</label>
|
||||
</t>
|
||||
<t t-call="website_sale.o_wsale_offcanvas_color_attribute"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -870,11 +887,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<div t-if="a.display_type == 'color'" class="mb-3">
|
||||
<t t-foreach="a.value_ids" t-as="v">
|
||||
<label t-attf-style="background-color:#{v.html_color or v.name}" t-attf-class="css_attribute_color mb-1 #{'active' if v.id in attrib_set else ''}">
|
||||
<input type="checkbox" name="attrib" t-att-value="'%s-%s' % (a.id,v.id)" t-att-checked="'checked' if v.id in attrib_set else None" t-att-title="v.name" />
|
||||
</label>
|
||||
</t>
|
||||
<t t-call="website_sale.o_wsale_offcanvas_color_attribute"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -95,9 +95,16 @@
|
||||
<t t-elif="ptal.attribute_id.display_type == 'color'">
|
||||
<ul t-att-data-attribute_id="ptal.attribute_id.id" t-attf-class="list-inline o_wsale_product_attribute #{'d-none' if single_and_custom else ''}">
|
||||
<li t-foreach="ptal.product_template_value_ids._only_active()" t-as="ptav" class="list-inline-item me-1">
|
||||
<label t-attf-style="background-color:#{ptav.html_color or ptav.product_attribute_value_id.name if not ptav.is_custom else ''}"
|
||||
t-attf-class="css_attribute_color #{'active' if ptav in combination else ''} #{'custom_value' if ptav.is_custom else ''} #{'transparent' if (not ptav.is_custom and not ptav.html_color) else ''}">
|
||||
<input type="radio"
|
||||
<t t-set="img_style"
|
||||
t-value="'background:url(/web/image/product.template.attribute.value/%s/image); background-size:cover;' % ptav.id if ptav.image else ''"
|
||||
/>
|
||||
<t t-set="color_style"
|
||||
t-value="'background:' + str(ptav.html_color or ptav.name if not ptav.is_custom else '')"
|
||||
/>
|
||||
<label t-attf-style="#{img_style or color_style}"
|
||||
t-attf-class="css_attribute_color #{'active' if ptav in combination else ''} #{'custom_value' if ptav.is_custom else ''} #{'transparent' if (not ptav.is_custom and not ptav.html_color) else ''}"
|
||||
>
|
||||
<input type="radio"
|
||||
t-attf-class="js_variant_change #{ptal.attribute_id.create_variant}"
|
||||
t-att-checked="ptav in combination"
|
||||
t-att-name="'ptal-%s' % ptal.id"
|
||||
|
||||
Reference in New Issue
Block a user