init git
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<section class="product-brand">
|
||||
<img :src="image" :alt="title" />
|
||||
<div>
|
||||
<h1>{{ title }}</h1>
|
||||
<h2>{{ name }}</h2>
|
||||
<h3>
|
||||
<i class="isax isax-verify"></i>
|
||||
<span>{{ category }}</span>
|
||||
</h3>
|
||||
<p>{{ desc }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Button v-for="(btn, i) in navigation" :key="i" v-bind="btn" />
|
||||
<Swiper v-bind="swiper">
|
||||
<SwiperSlide v-for="(item, i) in images" :key="i">
|
||||
<img :src="item.src" />
|
||||
</SwiperSlide>
|
||||
</Swiper>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import init from "../../initialize/brand";
|
||||
const { swiper, navigation } = init;
|
||||
|
||||
defineProps({
|
||||
image: { type: String, default: "/images/verity.svg" },
|
||||
title: { type: String, default: "VERITY" },
|
||||
name: { type: String, default: "وریتی" },
|
||||
category: { type: String, default: "محصولات الکترونیکی و دیجیتال" },
|
||||
desc: {
|
||||
type: String,
|
||||
default:
|
||||
"برند وریتی مارکی آشنا و شناخته شده در بازار IT کشور است که با در نظر گرفتن نیاز افراد مختلف جامعه همواره تلاش کرده محصولاتی با قیمت مناسب و سطح کیفی بالا را روانه بازار کند. این موضوع باعث استقبال خوب توزیع کنندگان و کاربران لوازم جانبی کامپیوتر و موبایل، از محصولات برند Verity شده است و این امر موجب شده تا بازار فروش خوبی را در داخل کشور به دست آورد. تنوع محصولات وریتی در سه زمینه راهکارهای ذخیره سازی، لوازم جانبی کامپیوتر و لوازم جانبی موبایل است.",
|
||||
},
|
||||
images: {
|
||||
type: Array,
|
||||
default: [
|
||||
{ src: "/images/verity-banner.svg" },
|
||||
{ src: "/images/verity-banner.svg" },
|
||||
{ src: "/images/verity-banner.svg" },
|
||||
{ src: "/images/verity-banner.svg" },
|
||||
{ src: "/images/verity-banner.svg" },
|
||||
],
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-brand {
|
||||
@apply w-full lg:h-[31.5rem] bg-[#FAFAFA] flex flex-col lg:flex-row gap-2 lg:gap-12 justify-center lg:pt-20 border-b-2 border-[#E5E5E5];
|
||||
|
||||
& > img {
|
||||
@apply w-28 h-28 object-contain object-center max-lg:hidden;
|
||||
}
|
||||
|
||||
& > div:nth-of-type(1) {
|
||||
@apply flex flex-col gap-1 lg:gap-3 max-lg:items-center max-lg:p-6;
|
||||
|
||||
h1 {
|
||||
@apply text-[#333333] text-[2em] leading-7 mt-3 font-bold font-vazir;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-[#999999] text-xl font-medium font-vazir;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply flex items-center gap-2 text-primary-600;
|
||||
|
||||
i {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply font-medium font-vazir leading-normal;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply w-full lg:w-[47.9rem] text-right text-[#333333] text-sm leading-5 max-lg:text-justify lg:text-lg font-normal font-vazir lg:leading-[2.3rem] mt-4;
|
||||
}
|
||||
}
|
||||
|
||||
& > div:nth-of-type(2) {
|
||||
@apply flex gap-3 h-max w-full lg:w-max max-w-full max-lg:order-first;
|
||||
|
||||
.swiper {
|
||||
@apply w-full max-w-[25rem] h-60 lg:h-[22rem] mx-0;
|
||||
|
||||
.swiper-wrapper {
|
||||
@apply w-full lg:w-max h-max;
|
||||
.swiper-slide {
|
||||
@apply w-max h-max lg:rounded-[0.6rem] overflow-hidden;
|
||||
img {
|
||||
@apply w-full h-60 lg:w-[25rem] lg:h-80 object-cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.swiper-pagination {
|
||||
@apply -bottom-1.5 max-lg:hidden;
|
||||
|
||||
.swiper-pagination-bullet {
|
||||
@apply w-7 rounded-full mx-1.5 bg-[#E5E5E5] opacity-100;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullet-active {
|
||||
@apply bg-[#47B556];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply self-center -mt-3 text-[2.5em] last-of-type:order-last max-lg:hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,275 @@
|
||||
<template>
|
||||
<aside class="product-filters">
|
||||
<h2>
|
||||
<i class="isax isax-setting-5"></i>
|
||||
<span>{{ $t('filters') }}</span>
|
||||
</h2>
|
||||
|
||||
<PanelMenu :model="categories" v-model:expanded-keys="expandedKeys">
|
||||
<template #submenuicon="{ active }">
|
||||
<i :class="'isax isax-arrow-' + (active ? 'up-2' : 'left-2')" />
|
||||
</template>
|
||||
</PanelMenu>
|
||||
|
||||
<Accordion expand-icon="isax left" collapse-icon="isax up">
|
||||
<AccordionTab :header="$t('brand')">
|
||||
<ul>
|
||||
<li v-for="(brand, i) in data.brands" :key="i">
|
||||
<Checkbox v-model="brands" :value="brand.link" :input-id="i" />
|
||||
<label :for="i"> {{ brand.title }} </label>
|
||||
</li>
|
||||
</ul>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
|
||||
<div>
|
||||
<label>{{ $t('availableInStock') }}</label>
|
||||
<InputSwitch v-model="stock" />
|
||||
</div>
|
||||
|
||||
<Accordion expand-icon="isax left" collapse-icon="isax up">
|
||||
<AccordionTab :header="$t('priceRange')">
|
||||
<ul>
|
||||
<li>
|
||||
<span>{{ $t('from') }}</span>
|
||||
<InputNumber v-model="prices[0]" :sync="true" />
|
||||
<span>{{ $t('priceUnit') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>{{ $t('until') }}</span>
|
||||
<InputNumber v-model="prices[1]" :sync="true" />
|
||||
<span>{{ $t('priceUnit') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<Slider v-model="prices" :min="1000" :max="200000000" :step="1000" range />
|
||||
</li>
|
||||
</ul>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { t } = inject('service')
|
||||
|
||||
const data = inject('data')
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const expandedKeys = reactive()
|
||||
|
||||
const catMap = (category) => {
|
||||
if (!category) return []
|
||||
const list = []
|
||||
category.forEach((cat) => {
|
||||
const temp = {
|
||||
_id: cat._id,
|
||||
label: cat.name,
|
||||
class: route.query.category == cat.link ? 'active' : '',
|
||||
command: ({ originalEvent }) => {
|
||||
if (cat.link && originalEvent.target.nodeName == 'SPAN') {
|
||||
router.push({ query: { ...route.query, category: cat.link } })
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cat.sub) temp.items = catMap(cat.sub)
|
||||
list.push(temp)
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
const stock = ref('stock' in route.query)
|
||||
const brands = ref([...(route.query?.brands?.split(',') ?? [])])
|
||||
const prices = ref([...(route.query?.prices?.split(',').map(i => parseInt(i)) ?? [0, 200000000])])
|
||||
|
||||
watch(brands, (v) => {
|
||||
const query = Object.assign({}, route.query)
|
||||
if (v?.length > 0)
|
||||
query.brands = v.join(',')
|
||||
else
|
||||
delete query.brands
|
||||
|
||||
router.push({ query })
|
||||
})
|
||||
watch(stock, (v) => {
|
||||
const query = Object.assign({}, route.query)
|
||||
if (v)
|
||||
query.stock = true
|
||||
else
|
||||
delete query.stock
|
||||
|
||||
router.push({ query })
|
||||
})
|
||||
let timer
|
||||
watch(prices, (v) => {
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
const query = Object.assign({}, route.query)
|
||||
const [$gte, $lte] = v
|
||||
query.prices = []
|
||||
if ($gte > 1000 || $lte < 200000000)
|
||||
query.prices = v.join(',')
|
||||
else
|
||||
delete query.prices
|
||||
|
||||
router.push({ query })
|
||||
}, 1000)
|
||||
}, { deep: true})
|
||||
const categoriesStroe = useCategoriesStore()
|
||||
const categories = reactive([
|
||||
{ label: t('categories'), items: catMap(categoriesStroe.items) }
|
||||
])
|
||||
|
||||
watch(() => route.query, () => (categories[0].items = catMap(categoriesStroe.items)))
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-filters {
|
||||
@apply w-full lg:w-[18.3rem] h-max rounded-[0.6rem] lg:border-2 border-[#EBEBEB];
|
||||
@apply flex flex-col lg:py-8 shrink-0;
|
||||
|
||||
h2 {
|
||||
@apply flex items-center gap-2 text-[#666666] px-6 mb-8;
|
||||
|
||||
span {
|
||||
@apply text-[1.4em] font-vazir;
|
||||
}
|
||||
|
||||
i {
|
||||
@apply text-[1.6em];
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(1)>div {
|
||||
@apply font-vazir text-lg font-bold flex flex-col px-6 pb-4 border-b-2 border-[#EBEBEB];
|
||||
|
||||
&>.p-toggleable-content {
|
||||
@apply w-[15.6rem] h-[18.8rem] overflow-y-auto pl-0 pt-4 self-end rtl;
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@apply my-6;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply pl-0;
|
||||
}
|
||||
|
||||
&>div>ul>li>.p-menuitem-content .p-menuitem-link {
|
||||
@apply pr-0;
|
||||
}
|
||||
|
||||
.p-menuitem-text {
|
||||
@apply text-stone-500 text-lg font-bold font-vazir pr-4;
|
||||
}
|
||||
|
||||
.active>.p-menuitem-content .p-menuitem-text {
|
||||
@apply text-primary-600 #{!important};
|
||||
}
|
||||
|
||||
.p-panelmenu-content {
|
||||
@apply border-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply px-6 py-4 border-b-2 border-[#EBEBEB] font-vazir;
|
||||
|
||||
.p-accordion-content {
|
||||
@apply pt-8 pb-4 rtl;
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-7;
|
||||
|
||||
li {
|
||||
@apply flex items-center gap-4;
|
||||
|
||||
.p-checkbox {
|
||||
@apply w-6 h-6 p-0.5 rounded border border-neutral-400;
|
||||
|
||||
.p-checkbox-box {
|
||||
@apply w-full h-full border-none;
|
||||
|
||||
svg {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
@apply text-[#666666] text-lg font-bold cursor-pointer select-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(3) {
|
||||
@apply flex justify-between p-6 border-b-2 border-[#EBEBEB];
|
||||
|
||||
label {
|
||||
@apply text-[#333333] text-lg font-bold font-vazir;
|
||||
}
|
||||
|
||||
.p-inputswitch {
|
||||
@apply w-12 h-6 bg-white;
|
||||
|
||||
.p-inputswitch-slider::before {
|
||||
@apply w-4 h-4 -mt-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(4) {
|
||||
@apply px-6 py-10 font-vazir;
|
||||
|
||||
.p-accordion-content {
|
||||
@apply pt-8 pb-4 rtl border-none;
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-6;
|
||||
|
||||
li {
|
||||
@apply flex items-end gap-4 max-w-full;
|
||||
|
||||
&>span {
|
||||
@apply text-[#666666] text-lg font-bold font-vazir grow;
|
||||
}
|
||||
|
||||
input {
|
||||
box-shadow: none;
|
||||
@apply text-center w-36 h-10 border-x-0 border-t-0 rounded-none #{!important};
|
||||
@apply border-b-2 border-[#EBEBEB] text-[#333333] text-[1.4em] font-vazir;
|
||||
|
||||
&:focus {
|
||||
@apply border-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
@apply w-full mt-4 rtl #{!important};
|
||||
|
||||
.p-slider-handle {
|
||||
@apply w-4 h-4 border-none bg-primary-600 -mt-2 focus:shadow-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-accordion-header-link {
|
||||
@apply bg-transparent border-none flex justify-between p-0;
|
||||
}
|
||||
|
||||
.p-panelmenu-header-content {
|
||||
@apply bg-transparent border-none;
|
||||
|
||||
.p-panelmenu-header-action {
|
||||
@apply flex p-0 justify-between
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<ul class="product-sort">
|
||||
<li>
|
||||
<i class="isax isax-sort"></i>
|
||||
<span>{{ $t('ordering') }}:</span>
|
||||
</li>
|
||||
<li v-for="(sort, i) in sorts" :key="i">
|
||||
<router-link :to="{ query: { ...$route.query, sort } }">
|
||||
<Button :label="$t(sort)" :disabled="sort == currentSort" link />
|
||||
</router-link>
|
||||
</li>
|
||||
<li>{{ products.length }} {{ $t('commodity') }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { products = [] } = inject('data')
|
||||
const { sorts } = inject('init')
|
||||
const route = useRoute()
|
||||
const currentSort = computed(() => route.query.sort || 'mostRelevant')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-sort {
|
||||
@apply flex items-center border-b-2 border-[#EBEBEB] whitespace-nowrap;
|
||||
|
||||
li:first-child {
|
||||
@apply flex items-center gap-2 text-[#333333] ml-1;
|
||||
|
||||
i {
|
||||
@apply text-base xl:text-[1.4em];
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-xs xl:text-[1.1em] font-bold font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
@apply mr-auto text-[#999999] text-[1.1em] font-bold font-vazir max-xl:hidden;
|
||||
}
|
||||
|
||||
li {
|
||||
button {
|
||||
.p-button-label {
|
||||
@apply text-[#999999] text-xs xl:text-[1.1em] font-bold font-vazir;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply opacity-100;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#47B556];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<section class="products-toolbar">
|
||||
<div>
|
||||
<template v-for="(btn, i) in btns.toolbar" :key="i">
|
||||
<Button v-bind="btn.props" @click="btn.click" />
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ $t("allCommodities") }}</span>
|
||||
<span>{{ data.products.length }} {{ $t("commodity") }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
const { btns } = inject('init')
|
||||
const data = inject('data')
|
||||
route.meta.data = data
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.products-toolbar {
|
||||
@apply w-full flex flex-col gap-4;
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply h-[3.8rem] flex items-center border-b border-[#E5E5E5];
|
||||
|
||||
button {
|
||||
@apply h-12 text-[#333333] first:-mr-5 -ml-3 #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-xs font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
@apply flex items-center justify-between;
|
||||
|
||||
span {
|
||||
@apply text-[#999999] text-[0.7em] font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<section class="product-wrapper">
|
||||
{{ data }}
|
||||
<h2 v-if="title">{{ $t(title) }}</h2>
|
||||
<div>
|
||||
<SearchFilters v-if="device == 'desktop'" />
|
||||
<div>
|
||||
<SearchSort v-if="device == 'desktop'" />
|
||||
<SearchToolbar v-if="device == 'mobile'" />
|
||||
<div>
|
||||
<template v-for="(product, i) in products" :key="i">
|
||||
<ProductCard :data="product" />
|
||||
</template>
|
||||
</div>
|
||||
<AppPagination :pages="pages" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { device } = inject('service')
|
||||
const { products, pages } = inject('data')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.product-wrapper {
|
||||
@apply w-full flex flex-col gap-4 lg:gap-[3.8rem] lg:pt-11 px-5;
|
||||
|
||||
&>h2 {
|
||||
@apply w-full flex justify-center lg:justify-start gap-3 lg:gap-6 items-center text-[#333333] text-base lg:text-2xl font-bold font-vazir;
|
||||
|
||||
&::before {
|
||||
@apply lg:content-['_'] h-8 w-1.5 rounded-full bg-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply w-full flex gap-6 justify-center;
|
||||
|
||||
&>div {
|
||||
@apply w-full container mx-0 flex flex-col gap-10;
|
||||
|
||||
|
||||
&>div {
|
||||
@apply w-full flex max-lg:flex-col lg:flex-wrap gap-3 lg:gap-[1.4rem];
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.product-card {
|
||||
@apply w-full h-[9.4rem] flex-row shadow-none border-0 rounded-none gap-4 p-0;
|
||||
@apply mt-[1.1rem] border-b border-[#E5E5E5];
|
||||
|
||||
&>img {
|
||||
@apply w-[7.4rem] h-[7.4rem] object-contain object-top self-start;
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply pb-6;
|
||||
|
||||
h2 {
|
||||
@apply mt-0 text-[0.7em];
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply left-0 bottom-3;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-xl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
@apply self-center mt-auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<div class="products-filters-popup">
|
||||
<div>
|
||||
<Button :icon="getIcon()" severity="secondary" rounded link @click="back()" />
|
||||
<h2>{{ $t(state) }}</h2>
|
||||
<Button v-if="state == 'filters'" link :label="$t('removeFilters')" @click="remove()" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Transition>
|
||||
<div v-if="state == 'filters'">
|
||||
<Button :label="$t('categories')" icon="isax isax-arrow-left-2" severity="secondary" link
|
||||
@click="state = 'categories'" />
|
||||
<Accordion expand-icon="isax left" collapse-icon="isax up">
|
||||
<AccordionTab :header="$t('brand')">
|
||||
<ul>
|
||||
<li v-for="(brand, i) in data.brands" :key="i">
|
||||
<Checkbox v-model="brands" :value="brand.link" :input-id="i" />
|
||||
<label :for="i"> {{ brand.title }} </label>
|
||||
</li>
|
||||
</ul>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
<div>
|
||||
<label>{{ $t("availableInStock") }}</label>
|
||||
<InputSwitch v-model="stock" />
|
||||
</div>
|
||||
<Accordion expand-icon="isax left" collapse-icon="isax up">
|
||||
<AccordionTab :header="$t('priceRange')">
|
||||
<ul>
|
||||
<li>
|
||||
<span>{{ $t('from') }}</span>
|
||||
<InputNumber v-model="prices[0]" :sync="true" />
|
||||
<span>{{ $t('priceUnit') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>{{ $t('until') }}</span>
|
||||
<InputNumber v-model="prices[1]" :sync="true" />
|
||||
<span>{{ $t('priceUnit') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<Slider v-model="prices" :min="1000" :max="200000000" :step="1000" range />
|
||||
</li>
|
||||
</ul>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
</div>
|
||||
<PanelMenu v-else :model="categories" v-model:expandedKeys="expandedKeys">
|
||||
<template #submenuicon="{ active }">
|
||||
<i :class="'isax isax-arrow-' + (active ? 'up-2' : 'left-2')" />
|
||||
</template>
|
||||
</PanelMenu>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button :label="loading ? $t('loading') : $t('viewProductCount', { count: data?.products.length })"
|
||||
icon="isax isax-arrow-up-2" severity="secondary" link @click="dialog.close()" :loading="loading" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
const data = computed(() => route.meta.data || {})
|
||||
const loading = computed(() => !route.meta.hasOwnProperty('data'))
|
||||
const router = useRouter()
|
||||
|
||||
const dialog = inject('dialogRef')
|
||||
|
||||
const state = ref("filters");
|
||||
const expandedKeys = ref({ });
|
||||
|
||||
const stock = ref('stock' in route.query)
|
||||
const brands = ref([...(route.query?.brands?.split(',') ?? [])])
|
||||
const prices = ref([...(route.query?.prices?.split(',').map(i => parseInt(i)) ?? [1000, 200000000])])
|
||||
|
||||
watch(brands, (v) => {
|
||||
const query = Object.assign({}, route.query)
|
||||
if (v?.length > 0)
|
||||
query.brands = v.join(',')
|
||||
else
|
||||
delete query.brands
|
||||
|
||||
router.push({ query })
|
||||
})
|
||||
watch(stock, (v) => {
|
||||
const query = Object.assign({}, route.query)
|
||||
if (v)
|
||||
query.stock = true
|
||||
else
|
||||
delete query.stock
|
||||
|
||||
router.push({ query })
|
||||
})
|
||||
let timer
|
||||
watch(prices, (v) => {
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
const query = Object.assign({}, route.query)
|
||||
const [$gte, $lte] = v
|
||||
query.prices = ''
|
||||
if ($gte > 1000 || $lte < 200000000)
|
||||
query.prices = v.join(',')
|
||||
else
|
||||
delete query.prices
|
||||
|
||||
router.push({ query })
|
||||
}, 500)
|
||||
}, { deep: true })
|
||||
|
||||
|
||||
const catMap = (items) => {
|
||||
if (!items) return []
|
||||
const list = []
|
||||
try {
|
||||
items.forEach((cat) => {
|
||||
const temp = {
|
||||
key: cat._id,
|
||||
label: cat.name,
|
||||
class: route.query.category == cat.link ? 'active' : '',
|
||||
command: ({ originalEvent }) => {
|
||||
if (cat.link && originalEvent.target.nodeName == 'SPAN') {
|
||||
router.push({ query: { category: cat.link } })
|
||||
back()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cat.sub.length > 0) temp.items = catMap(cat.sub)
|
||||
list.push(temp)
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
return list
|
||||
}
|
||||
const categories = ref(catMap(data.value?.categories))
|
||||
|
||||
const back = () => {
|
||||
if (state.value == "filters") dialog.value.close();
|
||||
else state.value = "filters";
|
||||
}
|
||||
|
||||
const remove = () => {
|
||||
router.push('/search')
|
||||
dialog.value.close()
|
||||
}
|
||||
|
||||
const getIcon = () => 'isax isax-' + (state.value == 'filters' ? 'close-circle' : 'arrow-right-1')
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.products-filters-popup {
|
||||
@apply w-full flex flex-col bg-white rounded-t-[0.6rem];
|
||||
|
||||
&>div:nth-of-type(1) {
|
||||
@apply flex items-center gap-3 p-6;
|
||||
|
||||
h2 {
|
||||
@apply text-black text-sm font-medium font-vazir;
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply -mx-4 -my-3 last:mr-auto last:text-primary-600;
|
||||
|
||||
&:first-child {
|
||||
@apply text-xl text-black #{!important};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:nth-of-type(2) {
|
||||
|
||||
&>div:not(.p-panelmenu) {
|
||||
@apply flex flex-col px-6;
|
||||
|
||||
&>button {
|
||||
@apply grow -mx-4 py-[1.1rem];
|
||||
|
||||
.p-button-label {
|
||||
@apply text-right text-zinc-800 text-sm font-bold font-vazir;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-lg text-zinc-800;
|
||||
}
|
||||
}
|
||||
|
||||
&>div.p-accordion {
|
||||
@apply py-[1.1rem] first-of-type:border-y-2 border-[#EBEBEB] font-vazir;
|
||||
|
||||
.isax {
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
.p-accordion-header-text {
|
||||
@apply text-zinc-800 text-sm font-bold font-vazir #{!important};
|
||||
}
|
||||
|
||||
&:nth-of-type(1) .p-accordion-content {
|
||||
@apply pt-8 pb-4 rtl;
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-4;
|
||||
|
||||
li {
|
||||
@apply flex items-center gap-3;
|
||||
|
||||
.p-checkbox {
|
||||
@apply w-5 h-5 p-0.5 rounded border border-neutral-400;
|
||||
|
||||
.p-checkbox-box {
|
||||
@apply w-full h-full border-none;
|
||||
|
||||
svg {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
@apply text-[#666666] text-sm font-bold cursor-pointer select-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(3) .p-accordion-content {
|
||||
@apply pt-8 pb-4 rtl;
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-10 w-full;
|
||||
|
||||
li {
|
||||
@apply flex justify-between items-end gap-[1.9rem] w-full;
|
||||
|
||||
&>span {
|
||||
@apply text-[#666666] text-sm font-bold font-vazir grow;
|
||||
}
|
||||
|
||||
input {
|
||||
box-shadow: none;
|
||||
@apply text-center w-[15rem] h-6 border-x-0 border-t-0 rounded-none #{!important};
|
||||
@apply border-b-2 border-[#EBEBEB] text-[#333333] text-[1.4em] font-vazir;
|
||||
|
||||
&:focus {
|
||||
@apply border-primary-600;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
@apply w-full mt-4;
|
||||
|
||||
.p-slider-handle {
|
||||
@apply w-4 h-4 border-none bg-primary-600 -mt-2 focus:shadow-none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&>div:not([class]) {
|
||||
@apply flex items-center justify-between py-[1.1rem] border-b-2 border-[#EBEBEB];
|
||||
|
||||
label {
|
||||
@apply text-[#333333] text-sm font-bold font-vazir;
|
||||
}
|
||||
|
||||
.p-inputswitch {
|
||||
@apply w-[2.1rem] h-[1.1rem] bg-white;
|
||||
|
||||
.p-inputswitch-slider::before {
|
||||
@apply w-[0.7rem] h-[0.7rem] -mt-[0.3rem];
|
||||
}
|
||||
|
||||
&.p-highlight .p-inputswitch-slider::before {
|
||||
@apply translate-x-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-panelmenu {
|
||||
@apply font-vazir text-sm font-bold flex flex-col gap-4 px-6 pb-4 rtl;
|
||||
|
||||
.p-panelmenu-header {
|
||||
@apply flex;
|
||||
|
||||
.p-panelmenu-header-action{
|
||||
@apply h-4 flex gap-1;
|
||||
}
|
||||
&:not(:has(i)){
|
||||
@apply pr-4;
|
||||
}
|
||||
}
|
||||
|
||||
.p-toggleable-content {
|
||||
@apply w-full h-full overflow-y-auto pr-4 pt-2 self-end rtl;
|
||||
|
||||
}
|
||||
|
||||
.p-menuitem-link {
|
||||
@apply h-8 flex gap-1 #{!important};
|
||||
|
||||
&:not(:has(i)){
|
||||
@apply pr-4;
|
||||
}
|
||||
}
|
||||
|
||||
.p-submenu-list{
|
||||
@apply pl-0;
|
||||
}
|
||||
|
||||
.active>.p-panelmenu-header .p-menuitem-text ,
|
||||
.active>.p-menuitem-content .p-menuitem-text ,
|
||||
|
||||
{
|
||||
@apply text-primary-600 #{!important};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&>div:nth-of-type(3) {
|
||||
@apply w-full mt-auto shadow-[0_-2px_5px_#0000000a];
|
||||
|
||||
button {
|
||||
@apply w-full px-6 rounded-none;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-right text-stone-500 text-sm font-bold font-vazir rtl;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-lg text-zinc-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="products-sort-popup">
|
||||
<div>
|
||||
<Button v-bind="btns.close.props" @click="dialog.close()" />
|
||||
<h2>{{ $t("sortBy") }}</h2>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="(item, i) in sorts" :key="i">
|
||||
<h3 @click="select(item)" v-ripple>
|
||||
{{ $t(item) }}
|
||||
</h3>
|
||||
<i v-if="sort == item" class="isax isax-tick-circle" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { meta } = useRoute()
|
||||
const { btns, sorts } = meta.init
|
||||
const dialog = inject('dialogRef')
|
||||
|
||||
const { query } = useRoute()
|
||||
const sort = ref(query.sort || "mostVisited");
|
||||
const router = useRouter()
|
||||
const select = (key) => {
|
||||
router.push({ path: '/search', query: {...query, sort: key} })
|
||||
dialog.value.close()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.products-sort-popup {
|
||||
@apply w-full h-max flex flex-col gap-9 p-6 bg-white rounded-t-[0.6rem];
|
||||
|
||||
&>div {
|
||||
@apply flex items-center gap-3;
|
||||
|
||||
&>button {
|
||||
@apply -mx-4 -my-3 text-[#333333] text-[1.4em] #{!important};
|
||||
}
|
||||
|
||||
&>h2 {
|
||||
@apply text-[#333333] text-sm font-medium font-vazir;
|
||||
}
|
||||
}
|
||||
|
||||
&>ul {
|
||||
@apply w-full flex flex-col gap-4;
|
||||
|
||||
&>li {
|
||||
@apply flex items-center justify-between pb-4 border-b last:border-b-0 text-[#333333];
|
||||
|
||||
h3 {
|
||||
@apply text-xs font-medium font-vazir overflow-hidden relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user