Merge branch 'main' of https://github.com/Byeto-Company/hossein_por_shop
This commit is contained in:
+17
-4
@@ -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
@@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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'],
|
||||||
|
|||||||
@@ -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 |
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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user