This commit is contained in:
marzban-dev
2026-02-24 14:40:21 +03:30
15 changed files with 554 additions and 37 deletions
+17 -4
View File
@@ -24,11 +24,20 @@ class HomeView(APIView):
sliders = SliderModel.objects.all() sliders = SliderModel.objects.all()
slider_ser = SliderSerializer(instance=sliders, many=True, context={'request': request}) slider_ser = SliderSerializer(instance=sliders, many=True, context={'request': request})
main_categories = MainCategoryModel.objects.all() main_categories = MainCategoryModel.objects.filter(show_in_home=True)
main_category_ser = MainCategorySerializer(instance=main_categories, many=True, context={'request': request}) main_category_ser = MainCategorySerializer(instance=main_categories, many=True, context={'request': request})
products_to_show = ProductModel.objects.filter(show=True) top_seller_products = ProductModel.objects.filter(show_in_top_seller=True)
product_ser = DynamicProductSerializer(instance=products_to_show, many=True, context={'request': request, 'view_type': 'list'}) top_seller_products_ser = DynamicProductSerializer(instance=top_seller_products, many=True, context={'request': request, 'view_type': 'list'})
lot_of_discount_products = ProductModel.objects.filter(show_in_lot_of_discount=True)
lot_of_discount_products_ser = DynamicProductSerializer(instance=lot_of_discount_products, many=True, context={'request': request, 'view_type': 'list'})
most_viewed_products = ProductModel.objects.filter(show_in_most_viewed=True)
most_viewed_products_ser = DynamicProductSerializer(instance=most_viewed_products, many=True, context={'request': request, 'view_type': 'list'})
trends_products = ProductModel.objects.filter(show_in_trends=True)
trends_products_ser = DynamicProductSerializer(instance=trends_products, many=True, context={'request': request, 'view_type': 'list'})
home_image = HomeImageModel.objects.all().first() home_image = HomeImageModel.objects.all().first()
home_image_ser = HomeImageSerializer(instance=home_image, context={'request': request}) home_image_ser = HomeImageSerializer(instance=home_image, context={'request': request})
@@ -39,7 +48,11 @@ class HomeView(APIView):
response = { response = {
'sliders': slider_ser.data, 'sliders': slider_ser.data,
'main_categories': main_category_ser.data, 'main_categories': main_category_ser.data,
'products': product_ser.data, 'products': [],
'top_seller_products': top_seller_products_ser.data,
'lot_of_discount_products': lot_of_discount_products_ser.data,
'most_viewed_products': most_viewed_products_ser.data,
'trends_products': trends_products_ser.data,
'difreance_section': home_image_ser.data, 'difreance_section': home_image_ser.data,
'show_case_slider': show_cases_ser.data 'show_case_slider': show_cases_ser.data
} }
+10 -13
View File
@@ -12,6 +12,7 @@ from unfold.widgets import UnfoldAdminColorInputWidget
from unfold.decorators import action, display from unfold.decorators import action, display
from utils.admin import ModelAdmin from utils.admin import ModelAdmin
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.utils.html import format_html
from .permissions import ProductDetailCategoryPermission, ProductAdminPermission, ProductVariantAdminPermission, ProductVariantInlineAdminPermission, InPackItemsAdminPermission, AttributeTypeAdminPermission, AttributeValueAdminPermission from .permissions import ProductDetailCategoryPermission, ProductAdminPermission, ProductVariantAdminPermission, ProductVariantInlineAdminPermission, InPackItemsAdminPermission, AttributeTypeAdminPermission, AttributeValueAdminPermission
from django import forms from django import forms
@@ -295,16 +296,16 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
inlines = [ProductVariantInLine] inlines = [ProductVariantInLine]
readonly_fields = ('slug', 'created_at') readonly_fields = ('slug', 'created_at')
search_fields = ['name', 'description', ] search_fields = ['name', 'description', ]
list_filter = ['show', ('category', RelatedDropdownFilter), 'show_in_bot', ('category__parent', RelatedDropdownFilter)] list_filter = [('category', RelatedDropdownFilter), 'show_in_bot', ('category__parent', RelatedDropdownFilter)]
list_filter_submit = True list_filter_submit = True
autocomplete_fields = ['related_products', 'shop', 'category'] autocomplete_fields = ['related_products', 'shop', 'category']
# compressed_fields = True # compressed_fields = True
warn_unsaved_form = True warn_unsaved_form = True
# list_per_page = 2 # list_per_page = 2
actions_list = ['redirect_to_learn', 'update_products_price'] actions_list = ['redirect_to_learn', 'update_products_price']
list_display = ['display_image', 'shop__shop_name','display_price', 'view', 'show', 'rating', 'category', 'created_at'] list_display = ['display_image', 'shop__shop_name','display_price', 'view', 'rating', 'category', 'created_at' ,'show_in_website', ]
fieldsets = ( fieldsets = (
('فیلد های اصلی', {'fields': ('name', 'description', 'category', 'image', 'related_products', 'show', 'shop', 'show_in_bot', 'bot_banner'), "classes": ["tab"],}), ('فیلد های اصلی', {'fields': ('name', 'description', 'category', 'image', 'related_products','show_in_trends', 'show_in_most_viewed', 'show_in_lot_of_discount', 'show_in_top_seller', 'shop', 'show_in_bot', 'bot_banner'), "classes": ["tab"],}),
('فیلد های سيو', {'fields': ('meta_description', 'meta_keywords', 'meta_rating', 'slug'), "classes": ["tab"],}), ('فیلد های سيو', {'fields': ('meta_description', 'meta_keywords', 'meta_rating', 'slug'), "classes": ["tab"],}),
('فیلد های مربوط به کاربر', {'fields': ('rating', 'view',), "classes": ["tab"],}), ('فیلد های مربوط به کاربر', {'fields': ('rating', 'view',), "classes": ["tab"],}),
# ('فیلد های ایتم های پک', {'fields': ('in_pack_items', ), "classes": ["tab"],}) # ('فیلد های ایتم های پک', {'fields': ('in_pack_items', ), "classes": ["tab"],})
@@ -324,13 +325,18 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
if request.user.is_superuser: if request.user.is_superuser:
return ['slug', 'created_at'] return ['slug', 'created_at']
else: else:
return ['show_in_bot', 'bot_banner', 'created_at', 'show', 'meta_description', 'meta_keywords', 'meta_rating', 'rating', 'view', 'slug'] return ['show_in_bot', 'bot_banner', 'created_at', 'show_in_top_seller','show_in_trends', 'show_in_most_viewed', 'show_in_lot_of_discount','meta_description', 'meta_keywords', 'meta_rating', 'rating', 'view', 'slug']
def display_price(self, obj): def display_price(self, obj):
if obj.variants.all().first(): if obj.variants.all().first():
return obj.variants.all().first().price return obj.variants.all().first().price
display_price.short_description = 'قیمت تومانی' display_price.short_description = 'قیمت تومانی'
def show_in_website(self, obj):
url = f"https://heymlz.com/product/{obj.slug}"
return format_html('<a href="{}" target="_blank" class="button">نمایش</a>', url)
show_in_website.short_description = 'نمایش در سایت'
@display(description='محصول', header=True) @display(description='محصول', header=True)
def display_image(self, instance): def display_image(self, instance):
if instance and instance.variants.first() and instance.variants.first().images.first(): if instance and instance.variants.first() and instance.variants.first().images.first():
@@ -398,15 +404,6 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
actions = ['bulk_update_subcategory_action'] actions = ['bulk_update_subcategory_action']
# @display(
# description=("نمایش در صفحه ی اصلی"),
# label={
# True: "danger",
# False: "success",
# },
# )
# def display_show(self, instance):
# return instance.show
@admin.register(MainCategoryModel) @admin.register(MainCategoryModel)
@@ -0,0 +1,18 @@
# Generated by Django 5.1.2 on 2026-02-20 15:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0070_enable_pg_trgm'),
]
operations = [
migrations.AddField(
model_name='maincategorymodel',
name='show_in_home',
field=models.BooleanField(default=False, verbose_name='نمایش در خانه'),
),
]
@@ -0,0 +1,58 @@
# Generated by Django 5.1.2 on 2026-02-20 15:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0034_add_telegram_chat_id_to_shop'),
('product', '0071_maincategorymodel_show_in_home'),
]
operations = [
migrations.RemoveIndex(
model_name='productmodel',
name='product_show_idx',
),
migrations.RemoveField(
model_name='productmodel',
name='show',
),
migrations.AddField(
model_name='productmodel',
name='show_in_lot_of_discount',
field=models.BooleanField(default=False, verbose_name='نمایش در پر تخفیف ها'),
),
migrations.AddField(
model_name='productmodel',
name='show_in_most_viewed',
field=models.BooleanField(default=False, verbose_name='نمایش در پر بازدید ها'),
),
migrations.AddField(
model_name='productmodel',
name='show_in_top_seller',
field=models.BooleanField(default=False, verbose_name='نمایش در پر فروش ها'),
),
migrations.AddField(
model_name='productmodel',
name='show_in_trends',
field=models.BooleanField(default=False, verbose_name='نمایش در ترند ها'),
),
migrations.AddIndex(
model_name='productmodel',
index=models.Index(fields=['show_in_trends'], name='product_show_in_trends_idx'),
),
migrations.AddIndex(
model_name='productmodel',
index=models.Index(fields=['show_in_most_viewed'], name='product_most_viewed_idx'),
),
migrations.AddIndex(
model_name='productmodel',
index=models.Index(fields=['show_in_lot_of_discount'], name='product_lot_of_discount_idx'),
),
migrations.AddIndex(
model_name='productmodel',
index=models.Index(fields=['show_in_top_seller'], name='product_show_in_top_seller_idx'),
),
]
+9 -2
View File
@@ -58,6 +58,7 @@ class MainCategoryModel(models.Model):
blank=True, null=True, verbose_name='ویدیو') blank=True, null=True, verbose_name='ویدیو')
parent = models.ForeignKey(UnitCategoryModel, on_delete=models.SET_NULL, parent = models.ForeignKey(UnitCategoryModel, on_delete=models.SET_NULL,
related_name='maincategorys', verbose_name='دسته‌بندی والد', null=True) related_name='maincategorys', verbose_name='دسته‌بندی والد', null=True)
show_in_home = models.BooleanField(default=False, verbose_name='نمایش در خانه')
class Meta: class Meta:
verbose_name = "دسته‌بندی اصلی" verbose_name = "دسته‌بندی اصلی"
verbose_name_plural = "دسته‌بندی‌هااصلی" verbose_name_plural = "دسته‌بندی‌هااصلی"
@@ -180,7 +181,10 @@ class ProductModel(models.Model):
description = models.TextField(verbose_name='توضیحات') description = models.TextField(verbose_name='توضیحات')
image = models.ImageField(upload_to='product_main/', null=True, blank=True) image = models.ImageField(upload_to='product_main/', null=True, blank=True)
rating = models.PositiveIntegerField(default=0, verbose_name='امتیاز') rating = models.PositiveIntegerField(default=0, verbose_name='امتیاز')
show = models.BooleanField(default=False, verbose_name='نمایش در خانه') show_in_trends = models.BooleanField(default=False, verbose_name='نمایش در ترند ها')
show_in_most_viewed = models.BooleanField(default=False, verbose_name='نمایش در پر بازدید ها')
show_in_lot_of_discount = models.BooleanField(default=False, verbose_name='نمایش در پر تخفیف ها')
show_in_top_seller = models.BooleanField(default=False, verbose_name='نمایش در پر فروش ها')
view = models.IntegerField(default=0, verbose_name='بازدید') view = models.IntegerField(default=0, verbose_name='بازدید')
slug = models.SlugField(max_length=255, unique=True, blank=True, null=True, allow_unicode=True, slug = models.SlugField(max_length=255, unique=True, blank=True, null=True, allow_unicode=True,
verbose_name='نام یکتا', help_text="این فیلد را خالی بگذارید") verbose_name='نام یکتا', help_text="این فیلد را خالی بگذارید")
@@ -223,7 +227,10 @@ class ProductModel(models.Model):
models.Index(fields=['category'], name='product_category_idx'), models.Index(fields=['category'], name='product_category_idx'),
models.Index(fields=['name'], name='product_name_idx'), models.Index(fields=['name'], name='product_name_idx'),
models.Index(fields=['created_at'], name='product_created_at_idx'), models.Index(fields=['created_at'], name='product_created_at_idx'),
models.Index(fields=['show'], name='product_show_idx'), models.Index(fields=['show_in_trends'], name='product_show_in_trends_idx'),
models.Index(fields=['show_in_most_viewed'], name='product_most_viewed_idx'),
models.Index(fields=['show_in_lot_of_discount'], name='product_lot_of_discount_idx'),
models.Index(fields=['show_in_top_seller'], name='product_show_in_top_seller_idx'),
models.Index(fields=['category', 'created_at'], models.Index(fields=['category', 'created_at'],
name='product_category_created_idx'), name='product_category_created_idx'),
models.Index(fields=['category', 'name'], models.Index(fields=['category', 'name'],
+393
View File
@@ -0,0 +1,393 @@
<!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>صورت‌حساب الکترونیکی - فروشگاه هیملز</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f8fafc;
color: #1e293b;
line-height: 1.6;
min-height: 100vh;
padding: 3rem 1rem;
font-size: 15px;
}
.pattern-bg {
position: fixed;
inset: 0;
width: 200%;
height: 200%;
left: -50%;
top: -50%;
background: url("logo-pattern.png") repeat;
background-size: 150px;
opacity: 0.05;
transform: rotate(12deg);
z-index: -1;
pointer-events: none;
}
.main-container {
width: 100%;
max-width: 1300px;
margin: 0 auto;
position: relative;
}
.qr-box {
position: absolute;
left: 48px;
bottom: 0;
transform: translateY(50%);
width: 140px;
aspect-ratio: 1;
border: 2px solid #6366f1;
background: white;
border-radius: 1rem;
overflow: hidden;
padding: 4px;
z-index: 10;
}
.qr-box img {
width: 100%;
height: 100%;
object-fit: contain;
}
.title-row {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.75rem;
font-weight: bold;
color: #6366f1;
gap: 2.25rem;
margin-bottom: 2.5rem;
text-align: center;
}
.title-row img {
width: 96px;
}
.grid-row {
display: grid;
grid-template-columns: 80px 1fr 300px;
gap: 8px;
margin-bottom: 8px;
}
.vertical-label {
background: #e0e7ff;
border: 2px solid #6366f1;
color: #6366f1;
border-radius: 1rem;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
writing-mode: vertical-rl;
text-orientation: mixed;
transform: rotate(180deg);
padding: 0.75rem 0.5rem;
font-size: 1.1rem;
}
.info-panel {
background: white;
border: 2px solid #6366f1;
border-radius: 1rem;
padding: 1.25rem;
}
.info-flex {
display: flex;
flex-wrap: wrap;
gap: 1.5rem 2rem;
}
.info-item {
display: flex;
gap: 0.4rem;
white-space: nowrap;
}
.info-item span:first-child {
font-weight: 600;
color: #4b5563;
}
.doc-info {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
/* Buyer row - full width */
.buyer-row {
height: 100px;
display: grid;
grid-template-columns: 80px 1fr;
gap: 8px;
margin-bottom: 16px;
}
.table-wrapper {
border: 2px solid #6366f1;
border-radius: 1rem;
background: white;
overflow: hidden;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
text-align: center;
}
th,
td {
padding: 0.9rem 0.6rem;
border: 2px solid #e0e7ff;
}
thead th {
background: #e0e7ff;
color: #6366f1;
font-weight: 600;
}
.summary-row td {
background: #f1f5f9;
font-weight: 600;
}
.grand-total-row {
background: #e0e7ff;
}
.grand-total-row td:last-child {
/* border-top: 3px solid #6366f1; */
font-weight: bold;
}
.signatures {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 1.5rem;
padding: 1.25rem;
border-top: 2px solid #6366f1;
height: 200px;
}
.sign-item {
display: flex;
gap: 0.4rem;
white-space: nowrap;
}
.sign-item span:first-child {
font-weight: 600;
color: #4b5563;
}
@media (max-width: 1100px) {
.grid-row {
grid-template-columns: 80px 1fr;
}
.doc-info {
margin-top: 1rem;
}
.buyer-row {
grid-template-columns: 80px 1fr;
}
.signatures {
grid-template-columns: repeat(2, 1fr);
}
.qr-box {
position: static;
margin: 2rem auto;
transform: none;
}
}
@media (max-width: 640px) {
.info-flex,
.signatures {
flex-direction: column;
gap: 0.8rem;
}
.title-row {
font-size: 1.4rem;
flex-direction: column;
gap: 0.5rem;
}
}
</style>
</head>
<body>
<div class="pattern-bg"></div>
<div class="main-container">
<!-- QR -->
<div class="qr-box">
<img src="qr-code.png" alt="QR" />
</div>
<!-- Header -->
<div class="title-row">
<div class="text-end" style="width: 100%;display: flex;justify-content: end;">صورت حساب الکترونیکی</div>
<div>
<img src="logo2.png" alt="Logo" />
</div>
<div class="text-start" style="width: 100%;display: flex;justify-content: start;">فروشگاه هیملز</div>
</div>
<!-- Seller + Document Info -->
<div class="grid-row">
<div class="vertical-label">فروشگاه<br />هی ملز</div>
<div class="info-panel">
<div class="info-flex">
<div class="info-item">
<span>فرستنده :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>شناسه ملی :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>شماره ثبت :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>شماره مجوز :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>نشانی شرکت :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>کد پستی :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>تلفن و فکس :</span><span>مقدار</span>
</div>
</div>
</div>
<div class="info-panel">
<div class="doc-info">
<div class="info-item">
<span>شماره فاکتور :</span><span>مقدار</span>
</div>
<div class="info-item"><span>تاریخ :</span><span>مقدار</span></div>
<div class="info-item">
<span>شماره پیگیری :</span><span>مقدار</span>
</div>
</div>
</div>
</div>
<!-- Buyer -->
<div class="buyer-row">
<div class="vertical-label">خریدار</div>
<div class="info-panel">
<div class="info-flex">
<div class="info-item"><span>خریدار :</span><span>مقدار</span></div>
<div class="info-item">
<span>شماره ملی :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>شماره تماس :</span><span>مقدار</span>
</div>
<div class="info-item">
<span>کد پستی :</span><span>مقدار</span>
</div>
<div class="info-item"><span>نشانی :</span><span>مقدار</span></div>
</div>
</div>
</div>
<!-- Table + Footer -->
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ردیف</th>
<th>شناسه کالا یا خدمات</th>
<th>شرح کالا یا خدمات</th>
<th>تعداد</th>
<th>مبلغ واحد (تومان)</th>
<th>مبلغ کل (تومان)</th>
<th>تخفیف (تومان)</th>
<th>مبلغ کل پس از تخفیف (تومان)</th>
<th>جمع مالیات و عوارض ارزش افزوده (تومان)</th>
<th>جمع کل پس از تخفیف و مالیات و عوارض (تومان)</th>
</tr>
</thead>
<tbody>
<!-- placeholder row -->
<tr>
<td>1</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
</tr>
<tr class="summary-row">
<td colspan="3">جمع کل</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
<td>---</td>
</tr>
<tr class="grand-total-row">
<td colspan="9" style="text-align: center; border-left: none">
جمع کل پس از کسر تخفیف با احتساب مالیات و عوارض (تومان)
</td>
<td>---</td>
</tr>
</tbody>
</table>
<div class="signatures">
<div class="sign-item">
<span>مهر و امضای فروشگاه :</span><span>مقدار</span>
</div>
<div class="sign-item">
<span>تاریخ تحویل :</span><span>مقدار</span>
</div>
<div class="sign-item">
<span>ساعت تحویل :</span><span>مقدار</span>
</div>
<div class="sign-item">
<span>روش های پرداخت :</span><span>مقدار</span>
</div>
<div class="sign-item">
<span>مهر و امضای خریدار :</span><span>مقدار</span>
</div>
</div>
</div>
</div>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

@@ -27,12 +27,12 @@ const onSwiper = (swiper: SwiperClass) => {
</script> </script>
<template> <template>
<section class="w-full flex flex-col gap-10 md:gap-[4rem] lg:container"> <section class="w-full flex flex-col gap-10 md:gap-16 lg:container">
<div class="w-full flex justify-between items-center max-lg:container"> <div class="w-full flex justify-between items-center max-lg:container">
<span class="text-black typo-h-6 md:typo-h-5 lg:typo-h-4"> <span class="text-black typo-h-6 md:typo-h-5 lg:typo-h-4">
{{ title }} {{ title }}
</span> </span>
<div class="flex-center gap-[.5rem]"> <div class="flex-center gap-2">
<button <button
@click="swiper_instance?.slidePrev()" @click="swiper_instance?.slidePrev()"
:disabled="swiper_instance?.isBeginning" :disabled="swiper_instance?.isBeginning"
@@ -41,7 +41,7 @@ const onSwiper = (swiper: SwiperClass) => {
? 'border-slate-200 cursor-not-allowed' ? 'border-slate-200 cursor-not-allowed'
: 'border-black cursor-pointer' : 'border-black cursor-pointer'
" "
class="size-[30px] md:size-[35px] lg:size-[2.75rem] rounded-full border-[1.5px] click-effect flex-center" class="size-[30px] md:size-[35px] lg:size-11 rounded-full border-[1.5px] click-effect flex-center"
> >
<Icon <Icon
name="ci:chevron-right" name="ci:chevron-right"
@@ -55,7 +55,7 @@ const onSwiper = (swiper: SwiperClass) => {
:class=" :class="
swiper_instance?.isEnd ? 'border-slate-200 cursor-not-allowed' : 'border-black cursor-pointer' swiper_instance?.isEnd ? 'border-slate-200 cursor-not-allowed' : 'border-black cursor-pointer'
" "
class="size-[30px] md:size-[35px] lg:size-[2.75rem] rounded-full border-[1.5px] click-effect flex-center" class="size-[30px] md:size-[35px] lg:size-11 rounded-full border-[1.5px] click-effect flex-center"
> >
<Icon <Icon
name="ci:chevron-left" name="ci:chevron-left"
@@ -108,7 +108,7 @@ const parallaxStyle = computed(() => {
</div> </div>
<div class="flex flex-col gap-1 px-2 items-start w-full text-black mt-4"> <div class="flex flex-col gap-1 px-2 items-start w-full text-black mt-4">
<span class="typo-sub-h-sm font-normal w-full truncate"> <span class="typo-sub-h-sm font-normal w-full line-clamp-2 leading-[175%]">
{{ title }} {{ title }}
</span> </span>
<div class="flex flex-col w-full mt-1"> <div class="flex flex-col w-full mt-1">
@@ -118,7 +118,11 @@ const parallaxStyle = computed(() => {
> >
{{ price }} {{ price }}
</span> </span>
<span v-if="priceAfter" class="whitespace-nowrap !font-bold">{{ priceAfter }}</span> <span
v-if="priceAfter"
class="whitespace-nowrap !font-bold"
>{{ priceAfter }}</span
>
</div> </div>
</div> </div>
</div> </div>
+7 -1
View File
@@ -16,6 +16,12 @@ const activeIndex = ref(0);
const slideElement = ref<HTMLDivElement | null>(null); const slideElement = ref<HTMLDivElement | null>(null);
const { width: slideWidth } = useElementSize(slideElement); const { width: slideWidth } = useElementSize(slideElement);
// computed
const slides = computed(() => {
return [...homeData.value!.main_categories, ...homeData.value!.main_categories];
});
// methods // methods
const onSwiper = (swiper: SwiperClass) => { const onSwiper = (swiper: SwiperClass) => {
@@ -101,7 +107,7 @@ const onSlideChange = (swiper: SwiperClass) => {
> >
<SwiperSlide <SwiperSlide
ref="slideElement" ref="slideElement"
v-for="(slide, index) in homeData!.main_categories" v-for="(slide, index) in slides"
:key="slide.id" :key="slide.id"
> >
<CategoryCard <CategoryCard
+4 -1
View File
@@ -2,7 +2,6 @@
import { useQuery } from "@tanstack/vue-query"; import { useQuery } from "@tanstack/vue-query";
import { API_ENDPOINTS, QUERY_KEYS } from "~/constants"; import { API_ENDPOINTS, QUERY_KEYS } from "~/constants";
import type { GetArticleResponse } from "~/composables/api/blog/useGetArticle";
// types // types
@@ -16,6 +15,10 @@ export type GetHomeDataResponse = {
video: string | null; video: string | null;
}[]; }[];
main_categories: Category[]; main_categories: Category[];
top_seller_products: ProductListItem[];
lot_of_discount_products: ProductListItem[];
most_viewed_products: ProductListItem[];
trends_products: ProductListItem[];
products: ProductListItem[]; products: ProductListItem[];
difreance_section: { difreance_section: {
image1: string; image1: string;
+21 -4
View File
@@ -2,7 +2,6 @@
// import // import
import useHomeData from "~/composables/api/home/useHomeData"; import useHomeData from "~/composables/api/home/useHomeData";
import ProductsGrid from "~/components/global/ProductsGrid.vue";
// state // state
@@ -23,13 +22,31 @@ if (response.isError) {
<template> <template>
<div class="w-full"> <div class="w-full">
<!-- <LoadingOverlay /> --> <!-- <LoadingOverlay /> -->
<Hero class="mb-20 max-md:mt-[80px]" /> <Hero class="mb-20 max-md:mt-20" />
<Preview /> <Preview />
<div class="py-20">
<ProductsSlider
title="محصولات پرتخفیف"
:products="homeData!.lot_of_discount_products"
/>
</div>
<div class="py-20">
<ProductsSlider
title="محصولات محبوب"
:products="homeData!.trends_products"
/>
</div>
<ProductsShowcase class="lg:mb-12" /> <ProductsShowcase class="lg:mb-12" />
<div class="py-[5rem]"> <div class="py-20">
<ProductsSlider <ProductsSlider
title="محصولات پرفروش" title="محصولات پرفروش"
:products="homeData!.products" :products="homeData!.top_seller_products"
/>
</div>
<div class="py-20">
<ProductsSlider
title="محصولات پربازدید"
:products="homeData!.most_viewed_products"
/> />
</div> </div>
<Categories class="mt-12" /> <Categories class="mt-12" />
+4 -3
View File
@@ -33,7 +33,7 @@ const personalData = ref<UpdateAccountRequest>({
first_name: account.value?.first_name ?? "", first_name: account.value?.first_name ?? "",
last_name: account.value?.last_name ?? "", last_name: account.value?.last_name ?? "",
phone: account.value?.phone ?? "", phone: account.value?.phone ?? "",
gender: account.value?.gender ?? undefined, gender: account.value?.gender || undefined,
email: account.value?.email ?? "", email: account.value?.email ?? "",
birth_date: account.value?.birth_date ?? "", birth_date: account.value?.birth_date ?? "",
}); });
@@ -64,7 +64,7 @@ const formRules = computed(() => {
required: helpers.withMessage("فیلد شماره تلفن الزامی می باشد", required), required: helpers.withMessage("فیلد شماره تلفن الزامی می باشد", required),
phoneValidator: helpers.withMessage( phoneValidator: helpers.withMessage(
"شماره تلفن وارد شده معتبر نمی باشد", "شماره تلفن وارد شده معتبر نمی باشد",
helpers.regex(/^0?[1-9][0-9]{9}$/) helpers.regex(/^0?[1-9][0-9]{9}$/),
), ),
}, },
}; };
@@ -99,7 +99,7 @@ const updateData = () => {
}, },
}); });
}, },
} },
); );
}; };
@@ -243,6 +243,7 @@ const handleSubmit = (withValidation: boolean) => {
<Input <Input
v-model="personalData.phone!" v-model="personalData.phone!"
variant="outlined" variant="outlined"
disabled
:error="formValidator$.phone.$error" :error="formValidator$.phone.$error"
/> />
</DataField> </DataField>