207 lines
4.3 KiB
Vue
207 lines
4.3 KiB
Vue
<!--
|
||||
|
|
Full-width horizontal product row for home «تازهها».
|
|||
|
|
Same fields and morph open as ProductCard — only the composition changes.
|
|||
|
|
-->
|
|||
|
|
<template>
|
|||
|
|
<article ref="root" class="pr" data-product-origin @click="open">
|
|||
|
|
<div class="pr__art">
|
|||
|
|
<img v-if="image" v-lazy="image" :alt="title" />
|
|||
|
|
<div v-else class="pr__artEmpty" aria-hidden="true">
|
|||
|
|
<Icon icon="vuesax-linear:diamonds" size="22" />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<button
|
|||
|
|
type="button"
|
|||
|
|
class="pr__fav"
|
|||
|
|
:class="{ 'is-on': favorites.isFavorite(id) }"
|
|||
|
|
:aria-label="favorites.isFavorite(id) ? 'حذف از علاقهمندی' : 'افزودن به علاقهمندی'"
|
|||
|
|
@click.stop="favorites.toggle(id)"
|
|||
|
|
>
|
|||
|
|
<Icon :icon="favorites.isFavorite(id) ? 'vuesax-linear:heart-fill' : 'vuesax-linear:heart'" size="15" />
|
|||
|
|
</button>
|
|||
|
|
|
|||
|
|
<span v-if="!is_exist" class="pr__flag">ناموجود</span>
|
|||
|
|
<span v-else-if="isPreOrder && qty === 0" class="pr__flag pr__flag--soft">پیشسفارش</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="pr__body">
|
|||
|
|
<h3 class="pr__title">{{ title }}</h3>
|
|||
|
|
<div class="pr__meta">
|
|||
|
|
<span class="pr__weight">{{ min_gram }} گرم</span>
|
|||
|
|
<span v-if="wage != null" class="pr__wage">اجرت {{ wageLabel }}٪</span>
|
|||
|
|
</div>
|
|||
|
|
<span class="pr__cta">مشاهده</span>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Icon icon="vuesax-linear:arrow-left-1" size="18" class="pr__chev" aria-hidden="true" />
|
|||
|
|
</article>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { computed, ref } from 'vue'
|
|||
|
|
import Icon from '../icon/Icon.vue'
|
|||
|
|
import { useProduct } from '../../composables/useProduct'
|
|||
|
|
import { openProduct } from '../../composables/useProductMorph'
|
|||
|
|
import { useFavoritesStore } from '../../stores/favoritesStore'
|
|||
|
|
import type { ProductSummary } from '../../api/client'
|
|||
|
|
|
|||
|
|
const props = defineProps<{ value: ProductSummary }>()
|
|||
|
|
|
|||
|
|
const favorites = useFavoritesStore()
|
|||
|
|
const { image, id, title, min_gram, wage, is_exist, qty, isPreOrder } = useProduct(props.value)
|
|||
|
|
|
|||
|
|
const root = ref<HTMLElement | null>(null)
|
|||
|
|
function open() {
|
|||
|
|
openProduct(props.value, root.value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const wageLabel = computed(() => {
|
|||
|
|
const w = wage.value
|
|||
|
|
if (w == null) return ''
|
|||
|
|
return w % 1 === 0 ? String(w) : w.toFixed(2)
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="less">
|
|||
|
|
.pr {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 12px;
|
|||
|
|
width: 100%;
|
|||
|
|
padding: 8px 12px 8px 10px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
background: @canvas;
|
|||
|
|
border: 1px solid @hairline;
|
|||
|
|
border-radius: 18px;
|
|||
|
|
transition: transform 0.2s ease, border-color 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr:active {
|
|||
|
|
transform: scale(0.99);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__art {
|
|||
|
|
position: relative;
|
|||
|
|
flex: 0 0 88px;
|
|||
|
|
width: 88px;
|
|||
|
|
height: 88px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
border-radius: 14px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
background: #f7f7f7;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__art img {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
max-width: 100%;
|
|||
|
|
max-height: 100%;
|
|||
|
|
padding: 4px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
object-fit: contain;
|
|||
|
|
object-position: center;
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
html.theme-dark .pr__art img {
|
|||
|
|
background: @photoMat;
|
|||
|
|
border-radius: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__artEmpty {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
display: grid;
|
|||
|
|
place-items: center;
|
|||
|
|
color: @inkFaint;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__fav {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 6px;
|
|||
|
|
left: 6px;
|
|||
|
|
width: 26px;
|
|||
|
|
height: 26px;
|
|||
|
|
display: grid;
|
|||
|
|
place-items: center;
|
|||
|
|
border: 0;
|
|||
|
|
cursor: pointer;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
color: @inkMuted;
|
|||
|
|
background: alpha(@canvas, 90%);
|
|||
|
|
backdrop-filter: blur(6px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__fav.is-on {
|
|||
|
|
color: @wine;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__flag {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 6px;
|
|||
|
|
right: 6px;
|
|||
|
|
padding: 3px 7px;
|
|||
|
|
border-radius: 999px;
|
|||
|
|
font-size: 9.5px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: #fff;
|
|||
|
|
background: @primary;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__flag--soft {
|
|||
|
|
background: @wineSoft;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__body {
|
|||
|
|
flex: 1 1 auto;
|
|||
|
|
min-width: 0;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__title {
|
|||
|
|
margin: 0;
|
|||
|
|
font-size: 13.5px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: @ink;
|
|||
|
|
line-height: 1.5;
|
|||
|
|
white-space: nowrap;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__meta {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 7px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__weight {
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: @inkSoft;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__wage {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: @inkFaint;
|
|||
|
|
padding-inline-start: 7px;
|
|||
|
|
border-inline-start: 1px solid @hairline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__cta {
|
|||
|
|
margin-top: 2px;
|
|||
|
|
font-size: 11.5px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: @wine;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pr__chev {
|
|||
|
|
flex: 0 0 auto;
|
|||
|
|
color: @inkFaint;
|
|||
|
|
}
|
|||
|
|
</style>
|