2026-09-18 12:54:45 +03:30
|
|
|
<!--
|
|
|
|
|
Product card for the redesigned home screen.
|
|
|
|
|
|
|
|
|
|
Shows only fields that genuinely exist on ProductSummary today: title, the
|
|
|
|
|
cheapest variant's rash weight, its making-charge (اجرت) percent, and the
|
|
|
|
|
catalogue image. No price is printed on the card, because a finished piece's
|
|
|
|
|
rial price is a function of the live gold rate plus wage and is computed at
|
|
|
|
|
order time, not stored on the product — printing one here would be invented.
|
|
|
|
|
-->
|
|
|
|
|
<template>
|
2026-10-04 16:18:56 +03:30
|
|
|
<article class="card" :class="{ 'card--wide': wide }" data-product-origin @click="emit('open', value)">
|
2026-09-18 12:54:45 +03:30
|
|
|
<div class="card__art">
|
|
|
|
|
<img v-lazy="image" :alt="title" />
|
|
|
|
|
<button type="button" class="card__fav" :class="{ 'is-on': favorites.isFavorite(id) }" @click.stop="favorites.toggle(id)">
|
|
|
|
|
<Icon :icon="favorites.isFavorite(id) ? 'vuesax-linear:heart-fill' : 'vuesax-linear:heart'" size="17" />
|
|
|
|
|
</button>
|
|
|
|
|
<span v-if="badge" class="card__badge">{{ badge }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="card__body">
|
|
|
|
|
<h3 class="card__title">{{ title }}</h3>
|
|
|
|
|
<div class="card__meta">
|
|
|
|
|
<span class="card__weight">{{ min_gram }} گرم</span>
|
|
|
|
|
<span v-if="wage != null" class="card__wage">اجرت {{ wageLabel }}٪</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</article>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed } from 'vue'
|
|
|
|
|
import Icon from '../icon/Icon.vue'
|
|
|
|
|
import { useProduct } from '../../composables/useProduct'
|
|
|
|
|
import { useFavoritesStore } from '../../stores/favoritesStore'
|
|
|
|
|
import type { ProductSummary } from '../../api/client'
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ value: ProductSummary; wide?: boolean; badge?: string }>()
|
|
|
|
|
const emit = defineEmits<{ (e: 'open', value: ProductSummary): void }>()
|
|
|
|
|
|
|
|
|
|
const favorites = useFavoritesStore()
|
|
|
|
|
const { image, id, title, min_gram, wage } = useProduct(props.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">
|
|
|
|
|
.card {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
background: @canvas;
|
|
|
|
|
border: 1px solid @hairline;
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card:active {
|
|
|
|
|
transform: scale(0.985);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__art {
|
|
|
|
|
position: relative;
|
|
|
|
|
aspect-ratio: 1 / 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
background: @hairlineSoft;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card--wide .card__art {
|
|
|
|
|
aspect-ratio: 4 / 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__art img {
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-width: 200px;
|
|
|
|
|
height: 100%;
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
object-position: center;
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__fav {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 9px;
|
|
|
|
|
left: 9px;
|
|
|
|
|
width: 30px;
|
|
|
|
|
height: 30px;
|
|
|
|
|
display: grid;
|
|
|
|
|
place-items: center;
|
|
|
|
|
border: 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
color: @inkMuted;
|
|
|
|
|
background: rgba(255, 255, 255, 0.9);
|
|
|
|
|
backdrop-filter: blur(6px);
|
|
|
|
|
transition: color 0.18s ease, transform 0.18s ease;
|
|
|
|
|
}
|
|
|
|
|
.card__fav.is-on {
|
|
|
|
|
color: @wine;
|
|
|
|
|
}
|
|
|
|
|
.card__fav:active {
|
|
|
|
|
transform: scale(0.9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__badge {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 9px;
|
|
|
|
|
right: 9px;
|
|
|
|
|
padding: 4px 9px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #fff;
|
|
|
|
|
background: @wine;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__body {
|
|
|
|
|
padding: 11px 12px 13px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: @ink;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__meta {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 7px;
|
|
|
|
|
padding-top: 7px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__weight {
|
|
|
|
|
font-size: 11.5px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: @inkSoft;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card__wage {
|
|
|
|
|
font-size: 10.5px;
|
|
|
|
|
color: @inkFaint;
|
|
|
|
|
padding-inline-start: 7px;
|
|
|
|
|
border-inline-start: 1px solid @hairline;
|
|
|
|
|
}
|
|
|
|
|
</style>
|