init git
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<section class="breadcrumbs">
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<router-link :to="item.link">
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({ items: { type: Array, default: [] } });
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.breadcrumbs {
|
||||
@apply w-full flex max-lg:px-6;
|
||||
|
||||
div {
|
||||
@apply overflow-x-auto h-max;
|
||||
|
||||
&::-webkit-scrollbar{
|
||||
@apply hidden;
|
||||
}
|
||||
ul {
|
||||
@apply flex items-center gap-3.5 lg:gap-6 w-max;
|
||||
|
||||
li {
|
||||
@apply relative text-[0.7em] lg:text-[1.1em];
|
||||
|
||||
a {
|
||||
@apply text-[#999999] font-bold font-vazir;
|
||||
}
|
||||
|
||||
&:not(:last-child)::after {
|
||||
@apply content-['/'] absolute -left-2 lg:-left-3.5;
|
||||
}
|
||||
|
||||
&:last-child a {
|
||||
@apply text-[#666666];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<ul v-if="pages > 1" class="pagination">
|
||||
<li v-for="i in [-1, 1]" :key="i">
|
||||
<router-link
|
||||
:to="{ query: { ...$route.query, page: page + i } }"
|
||||
:class="{ 'pointer-events-none': page + i < 1 || page + i > pages }"
|
||||
>
|
||||
<Button :icon="getIcon(i)" link />
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-for="(item, i) in items" :key="i" :style="{ order: i + 2 }">
|
||||
<router-link
|
||||
:to="{ query: { ...$route.query, page: item } }"
|
||||
:class="{ 'pointer-events-none': item == page }"
|
||||
>
|
||||
<Button :label="item" link :disabled="item == page" />
|
||||
</router-link>
|
||||
</li>
|
||||
<li v-show="pages > 5" :style="{ order: pages / 2 > page ? 6 : 2 }">
|
||||
<span>...</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps(['pages'])
|
||||
|
||||
const route = useRoute()
|
||||
const page = computed(() => +(route.query.page || 1))
|
||||
|
||||
const items = ref([])
|
||||
|
||||
const init = () => {
|
||||
items.value = [page.value]
|
||||
let temp = 1
|
||||
while (items.value.length < 5) {
|
||||
if (page.value - temp > 0) items.value.unshift(page.value - temp)
|
||||
if (page.value + temp <= props.pages) items.value.push(page.value + temp)
|
||||
temp++
|
||||
if (temp > 10) break
|
||||
}
|
||||
if (props.pages > 5) {
|
||||
if (props.pages / 2 > page.value) items.value.push(props.pages)
|
||||
else items.value.unshift(1)
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
||||
watch(() => page.value, init)
|
||||
|
||||
const getIcon = (i) => 'isax isax-arrow-' + (i > 0 ? 'right-3' : 'left-2')
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.pagination {
|
||||
box-shadow: 0px 2px 20px rgba(1, 86, 153, 0.08);
|
||||
@apply bg-white rounded-lg gap-2 lg:gap-4 flex flex-row-reverse items-center justify-between px-2 lg:px-4;
|
||||
@apply w-auto h-10 lg:h-[3.7rem];
|
||||
|
||||
li {
|
||||
@apply h-full flex items-center;
|
||||
|
||||
button {
|
||||
@apply w-5 h-5 lg:w-6 lg:h-6 p-0 rounded-sm #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-sm lg:text-base font-bold font-vazir lg:leading-tight text-[#8C8C8C] lg:mt-0.5;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#8C8C8C] lg:mt-[0.2rem] text-xs;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
@apply bg-[#379956] opacity-100;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
// &:has(.p-button-icon){
|
||||
// @apply max-lg:bg-[#F2F2F2];
|
||||
// }
|
||||
}
|
||||
|
||||
& > span {
|
||||
@apply text-[#8C8C8C] font-medium font-vazir leading-[1.7rem] mt-0.5;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
@apply order-last;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="about-us">
|
||||
<div>
|
||||
<img src="/images/logo.svg" alt="logo" />
|
||||
<p>{{ $t("aboutUsDesc") }}</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="item in items" :key="item">
|
||||
<i :class="`isax isax-${item.icon}`" />
|
||||
<span>{{ $t(item.title) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const items = reactive([
|
||||
{ icon: "call", title: "aboutUsPhone" },
|
||||
{ icon: "sms", title: "aboutUsPostalCode" },
|
||||
{ icon: "location", title: "aboutUsAddress" },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
footer .about-us {
|
||||
@apply flex flex-col gap-6 p-8 font-vazir;
|
||||
@apply w-full h-auto bg-zinc-100 rounded-2xl border-2 border-neutral-200;
|
||||
@apply sm:w-[32.7rem] sm:h-[31.3rem];
|
||||
@apply lg:flex-row lg:w-[57.7rem] lg:h-auto;
|
||||
@apply xl:flex-col xl:w-[32.7rem] xl:h-[31.3rem];
|
||||
|
||||
&>div {
|
||||
@apply flex flex-col gap-6;
|
||||
|
||||
img {
|
||||
@apply w-60 h-10;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply w-auto sm:w-[28.1rem] text-zinc-800 text-lg leading-[1.9rem];
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex flex-col gap-4 mt-4;
|
||||
|
||||
li {
|
||||
@apply flex gap-3 h-max;
|
||||
|
||||
i {
|
||||
@apply text-2xl h-[1.9rem];
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-zinc-800 text-base font-medium leading-[1.9rem];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="customer-services">
|
||||
<h2>{{ $t("customer-services") }}</h2>
|
||||
<ul>
|
||||
<li v-for="(service, i) in services" :key="i">
|
||||
<a :href="service.url">
|
||||
<span>{{ $t(service.title) }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const services = reactive([
|
||||
{ url: "/contact-us", title: "contactUs" },
|
||||
{ url: "/about-us", title: "aboutUs" },
|
||||
{ url: "/search", title: "shop" },
|
||||
{ url: "/blog", title: "media" },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
footer .customer-services {
|
||||
@apply flex flex-col gap-4 w-max font-vazir items-center sm:items-start;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-xl font-bold leading-[1.9rem];
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex gap-4 sm:flex-col justify-between sm:gap-2;
|
||||
|
||||
li {
|
||||
a {
|
||||
span {
|
||||
@apply text-stone-500 text-[0.9em] leading-[1.9rem] hover:text-primary-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<footer>
|
||||
<AppDesktopFooterAboutUs />
|
||||
<div>
|
||||
<AppDesktopFooterNewsletters />
|
||||
<AppDesktopFooterSocialNetworks />
|
||||
<div>
|
||||
<AppDesktopFooterCustomerServices />
|
||||
<AppDesktopFooterPurchaseGuide />
|
||||
<AppDesktopFooterTrustSymbols />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="scss">
|
||||
footer {
|
||||
@apply w-full flex flex-col sm:items-center justify-center bg-[#F5F5F5];
|
||||
@apply gap-4 px-3 py-12;
|
||||
@apply xl:flex-row xl:gap-[6.7rem] 2xl:py-[6.3rem];
|
||||
|
||||
&>div:not([class]) {
|
||||
@apply flex flex-col gap-6;
|
||||
|
||||
&>div:not([class]) {
|
||||
@apply flex flex-col items-center sm:items-start sm:flex-row justify-center gap-8 mt-4 lg:justify-between xl:gap-6 xl:mt-10;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="newsletters">
|
||||
<h2>{{ $t("newslettersDesc") }}</h2>
|
||||
<form>
|
||||
<div>
|
||||
<i class="isax isax-sms" />
|
||||
<span>|</span>
|
||||
<input v-model="email" :class="{ '!ltr': email }" :placeholder="$t('emailAddress')" />
|
||||
</div>
|
||||
<Button :label="$t('submit')" :loading="loading" @click="submit()" />
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { toast, t } = inject('service')
|
||||
const loading = ref(false)
|
||||
const email = ref()
|
||||
|
||||
const submit = async () => {
|
||||
if (email.value.includes('@')) {
|
||||
loading.value = true;
|
||||
const { status, error } = await useFetch('/api/news', { method: 'post', body: { email: email.value } })
|
||||
loading.value = false;
|
||||
email.value = '';
|
||||
|
||||
if (status.value == 'success')
|
||||
toast.add({
|
||||
life: 3000,
|
||||
severity: 'success',
|
||||
summary: t('success'),
|
||||
detail: t('submitedSuccessfully')
|
||||
})
|
||||
else
|
||||
toast.add({
|
||||
life: 3000,
|
||||
severity: 'error',
|
||||
summary: t('error'),
|
||||
detail: error.value
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
footer .newsletters {
|
||||
@apply bg-zinc-100 rounded-xl border-2 border-neutral-200;
|
||||
@apply flex flex-col items-center justify-between p-3 font-vazir;
|
||||
@apply w-full h-auto gap-4 py-4;
|
||||
@apply lg:w-[57.7rem] lg:h-[4.5rem] lg:flex-row;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-lg font-medium leading-[1.9rem] mr-3;
|
||||
}
|
||||
|
||||
form {
|
||||
@apply flex gap-2;
|
||||
|
||||
div {
|
||||
@apply w-[16.9rem] md:w-[21.5rem] h-12 bg-neutral-200 rounded-lg flex gap-3 items-center p-3;
|
||||
|
||||
i {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply h-12 bg-transparent w-full rtl text-neutral-600 font-medium outline-none;
|
||||
|
||||
&:placeholder {
|
||||
@apply text-neutral-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-[4.5rem] h-12 bg-[#47B556] rounded-md text-white text-base font-medium #{!important};
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="purchase-guide">
|
||||
<h2>{{ $t("purchaseGuideFromAM") }}</h2>
|
||||
<ul>
|
||||
<li v-for="(guide, i) in guides" :key="i">
|
||||
<a :href="guide.url">
|
||||
<span>{{ $t(guide.title) }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const guides = reactive([
|
||||
{ url: "/", title: "howToPlaceOrder" },
|
||||
{ url: "/", title: "orderSendingProcedure" },
|
||||
{ url: "/", title: "paymentMethods" },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
footer .purchase-guide {
|
||||
@apply flex flex-col gap-4 sm:gap-[1.4rem] w-max font-vazir items-center sm:items-start;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-xl font-bold leading-[1.9rem];
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex gap-4 sm:flex-col sm:gap-2;
|
||||
|
||||
li {
|
||||
a {
|
||||
span {
|
||||
@apply text-stone-500 text-[0.9em] leading-[1.9rem] hover:text-primary-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="social-networks">
|
||||
<h2>{{ $t("followUs") }}</h2>
|
||||
<ul>
|
||||
<li v-for="({url, icon}, i) in socials" :key="i">
|
||||
<a :href="url" target="_blank">
|
||||
<img :src="`/icons/${icon}.svg`" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const socials = reactive([
|
||||
{ url: "/", icon: "linkedin" },
|
||||
{ url: "/", icon: "telegram" },
|
||||
{ url: "/", icon: "whatsapp" },
|
||||
{ url: "/", icon: "instagram" },
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
footer .social-networks {
|
||||
@apply bg-zinc-100 rounded-xl border-2 border-neutral-200;
|
||||
@apply flex flex-col items-center justify-between px-6;
|
||||
@apply w-full h-auto gap-4 py-4;
|
||||
@apply lg:w-[57.7rem] lg:h-[4.5rem] md:flex-row;
|
||||
|
||||
h2 {
|
||||
@apply text-zinc-800 text-lg font-medium font-vazir leading-[1.9rem];
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply flex gap-2;
|
||||
|
||||
li {
|
||||
@apply w-max h-max;
|
||||
a {
|
||||
@apply w-10 h-10 bg-[#B2B2B2] hover:bg-[#43E97B] rounded-full flex items-center justify-center;
|
||||
img {
|
||||
@apply h-6 w-6 object-scale-down;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="trust-symbols">
|
||||
<h2>{{ $t('symbolOfTrust')}}</h2>
|
||||
<div>
|
||||
<picture v-for="(symbol, i) in symbols" :key="i">
|
||||
<img :src="`/images/${symbol}.svg`" />
|
||||
</picture>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const symbols = reactive(['samandehi', 'kasbokar'])
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
footer .trust-symbols{
|
||||
@apply flex flex-col items-center gap-4;
|
||||
@apply sm:items-start;
|
||||
|
||||
h2{
|
||||
@apply text-zinc-800 text-xl font-bold font-vazir leading-[1.9rem];
|
||||
}
|
||||
|
||||
div{
|
||||
@apply flex gap-4;
|
||||
|
||||
picture{
|
||||
@apply flex w-24 h-32 bg-white rounded-lg border border-neutral-300;
|
||||
|
||||
img{
|
||||
@apply m-auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<header class="header-desktop">
|
||||
<router-link to="/">
|
||||
<img src="/images/logo.svg" alt="Asan Market" />
|
||||
</router-link>
|
||||
<AppDesktopHeaderNavigation />
|
||||
<router-link v-if="!logged" :to="{ path: '/auth/login' }">
|
||||
<Button v-bind="btns.login.props" />
|
||||
</router-link>
|
||||
<div v-else>
|
||||
<template v-for="({ props, component }, key) in btns.logged" :key="key">
|
||||
<Button v-bind="props" @click="overlay($event, component)" />
|
||||
</template>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import init from '../../../../initialize/header'
|
||||
const { btns } = init()
|
||||
|
||||
const logged = useCookie('token')
|
||||
const { popup } = inject('service')
|
||||
|
||||
const overlay = (event, is) => {
|
||||
popup.value.is = is
|
||||
popup.value.toggle(event)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-desktop {
|
||||
box-shadow: 0px 2px 20px rgba(0.22, 75.27, 129.62, 0.14);
|
||||
@apply w-full h-[3.3rem] lg:h-24 bg-white shadow border-b pl-3 lg:px-3 2xl:p-6 flex items-center justify-between;
|
||||
@apply sticky top-0 z-10 xl:gap-4;
|
||||
|
||||
&>a>img {
|
||||
@apply w-60 h-10;
|
||||
}
|
||||
|
||||
&>a {
|
||||
@apply shrink-0;
|
||||
|
||||
&>button {
|
||||
@apply flex items-center text-white;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
.p-button-label {
|
||||
@apply font-bold font-iran-sans tracking-widest whitespace-nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&>div {
|
||||
@apply flex items-center gap-6 2xl:gap-12 relative;
|
||||
|
||||
&::after {
|
||||
@apply content-['_'] w-0.5 h-[1.3rem] bg-[#DCDCDC] absolute left-[3.6rem] 2xl:left-[4.4rem];
|
||||
}
|
||||
|
||||
button {
|
||||
@apply border-[#CCCCCC] h-12 rounded-[0.6rem] #{!important};
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#333333] text-lg font-medium font-vazir whitespace-nowrap;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#333333] text-2xl;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
@apply pl-6 #{!important};
|
||||
|
||||
.p-button-icon {
|
||||
@apply ml-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@apply w-12 p-2 relative;
|
||||
|
||||
.p-badge {
|
||||
@apply absolute w-4 h-4 bg-[#B3261E] top-0.5 right-0.5 flex items-center justify-center;
|
||||
@apply text-white text-[0.7em] font-medium leading-none tracking-wide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-blockui-container {
|
||||
@apply absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<Transition>
|
||||
<nav v-if="visible" class="mega-menu" @click.self="visible = false">
|
||||
<TransitionGroup name="list" tag="section">
|
||||
<template v-for="(list, i) in menu" :key="i">
|
||||
<ul v-if="list?.length">
|
||||
<li v-for="(item, j) in list" :key="j" @mouseover="open(item, i)">
|
||||
<router-link :to="{ path: '/search', query: { category: item.link } }">
|
||||
<Button v-bind="getProps(item, i)" />
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</TransitionGroup>
|
||||
</nav>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const visible = defineModel()
|
||||
const store = useCategoriesStore()
|
||||
const menu = reactive([])
|
||||
|
||||
const getProps = (item, i) => {
|
||||
const temp = { label: item.name, link: true }
|
||||
if (i === 0) {
|
||||
temp.iconPos = 'right'
|
||||
temp.icon = 'isax ' + item.icon
|
||||
// temp.disabled = menu[1] == item.sub
|
||||
} else if (i === 1 && item.sub.length > 0) {
|
||||
temp.icon = 'isax isax-arrow-left-2'
|
||||
// temp.disabled = menu[2] == item.sub
|
||||
}
|
||||
return temp
|
||||
}
|
||||
|
||||
const open = (item, i) => {
|
||||
if (i < 2) {
|
||||
delete menu[i + 2]
|
||||
menu[i + 1] = item.sub
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let cat = store.items
|
||||
while (true) {
|
||||
menu.push(cat)
|
||||
if (cat[0]?.sub) cat = cat[0].sub
|
||||
else break
|
||||
}
|
||||
})
|
||||
const route = useRoute()
|
||||
watch(() => route.query, () => visible.value = false)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.mega-menu {
|
||||
@apply fixed inset-x-0 z-[1] bottom-0 top-24 bg-black/40;
|
||||
|
||||
&>section {
|
||||
@apply flex h-[32.4rem] w-full bg-white shadow-inner;
|
||||
|
||||
ul:nth-of-type(1) {
|
||||
@apply flex flex-col gap-2 py-3 px-4 w-[16.5rem] h-[26.8rem] z-[10];
|
||||
|
||||
li {
|
||||
button {
|
||||
@apply w-full h-12 justify-end;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#292D32] text-lg font-medium font-vazir grow-0;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#292D32] text-2xl ml-4;
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
@apply text-primary-600;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply opacity-100;
|
||||
|
||||
* {
|
||||
@apply text-primary-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul:nth-of-type(2) {
|
||||
@apply border-x-2 border-[#E5E5E5] w-[16.5rem] h-[32.4rem] flex flex-col;
|
||||
|
||||
li {
|
||||
@apply border-b border-[#E5E5E5];
|
||||
|
||||
button {
|
||||
@apply w-full h-[3.7rem] justify-between px-2.5;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#292D32] text-lg font-vazir grow-0 ml-auto;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#292D32] text-sm transition-transform;
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
@apply text-primary-600;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply opacity-100 bg-[#FAFAFA];
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#333333] font-medium;
|
||||
}
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#333333] -rotate-90;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul:nth-of-type(3) {
|
||||
@apply flex flex-col;
|
||||
|
||||
li {
|
||||
@apply px-4;
|
||||
|
||||
button {
|
||||
@apply w-full h-[3.8rem] justify-end;
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#333333] text-lg font-normal font-vazir grow-0;
|
||||
}
|
||||
|
||||
&:hover * {
|
||||
@apply text-primary-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-enter-active,
|
||||
.list-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.list-enter-from,
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
.menu-enter-active,
|
||||
.menu-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
div {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-enter-from,
|
||||
.menu-leave-to {
|
||||
opacity: 0;
|
||||
|
||||
div {
|
||||
transform: translateX(30px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: opacity 0.1s ease;
|
||||
}
|
||||
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<ul class="mini-menu">
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<router-link :to="item.to" @click="popup.hide()">
|
||||
<Button v-bind="item.props" iconPos="right" severity="secondary" link />
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<Button :label="$t('logout')" icon="isax isax-logout-1" iconPos="right" severity="secondary" link
|
||||
@click="logout()" />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const items = reactive([
|
||||
{
|
||||
to: "/panel/profile",
|
||||
props: { label: "پروفایل کاربری", icon: "isax isax-user-square" },
|
||||
},
|
||||
{ to: "/panel/favorites", props: { label: "علاقه مندی ها", icon: "isax isax-heart" } },
|
||||
{
|
||||
to: "/panel/comments",
|
||||
props: { label: "نظرات من", icon: "isax isax-message-text4" },
|
||||
},
|
||||
{ to: "/panel/factors", props: { label: "فاکتورها", icon: "isax isax-receipt-2-1" } },
|
||||
{
|
||||
to: "/panel/moeen",
|
||||
props: { label: "فاکتور معین", icon: "isax isax-archive-book4" },
|
||||
},
|
||||
{ to: "/panel/notifications", props: { label: "اعلان ها", icon: "isax isax-notification" } },
|
||||
{
|
||||
to: "/panel/orders",
|
||||
props: { label: "سفارشات", icon: "isax isax-shopping-cart" },
|
||||
},
|
||||
])
|
||||
|
||||
const { popup } = inject('service')
|
||||
|
||||
const { dialog } = inject('service')
|
||||
const logout = () => {
|
||||
popup.value.hide();
|
||||
dialog.show(resolveComponent('PanelDialogLogout'))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.mini-menu {
|
||||
@apply flex flex-col gap-3 px-4 pt-[1.4rem] rtl shadow-[0px_2px_10px_#004b8229];
|
||||
@apply w-[13.4rem] h-[27.8rem] bg-white rounded-2xl shadow border;
|
||||
|
||||
li {
|
||||
button {
|
||||
@apply h-10 py-1.5 justify-end #{!important};
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#333333] text-2xl ml-3;
|
||||
}
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#333333] text-lg font-medium font-vazir grow-0 whitespace-nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-overlaypanel:has(.mini-menu) {
|
||||
@apply -translate-x-8;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<ul class="navigation">
|
||||
<li>
|
||||
<Button :label="$t('product-categories')" iconPos="right" severity="secondary" text icon="isax isax-menu-1"
|
||||
@click="visible = !visible" />
|
||||
</li>
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<component :is="item.link.includes('http') ? 'a' : 'router-link'" :to="item.link" :href="item.link">
|
||||
<Button iconPos="right" severity="secondary" text v-bind="item.props" />
|
||||
</component>
|
||||
</li>
|
||||
<li>
|
||||
<Button iconPos="right" severity="secondary" text icon="isax isax-search-normal-1"
|
||||
@click="$router.push('/search')" />
|
||||
</li>
|
||||
</ul>
|
||||
<AppDesktopHeaderMegaMenu v-model="visible" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const items = ref([])
|
||||
const visible = ref(false)
|
||||
|
||||
const { status, data, error } = await useFetch('/api/headers')
|
||||
|
||||
if (status.value == 'success')
|
||||
items.value = data.value.map((item) => {
|
||||
item.props = { label: item.title, icon: 'isax ' + item.icon }
|
||||
return item
|
||||
})
|
||||
else throw createError(error.value)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.navigation {
|
||||
@apply flex items-center justify-center lg:gap-6 2xl:justify-between w-full max-w-[70.6rem];
|
||||
|
||||
li:first-child,
|
||||
li:last-child {
|
||||
@apply flex items-center relative;
|
||||
}
|
||||
|
||||
li:first-child::after,
|
||||
li:last-child::before {
|
||||
@apply content-['_'] w-0.5 h-[1.3rem] bg-[#DCDCDC] absolute;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
@apply -ml-1;
|
||||
|
||||
&::after {
|
||||
@apply left-0 2xl:-left-3;
|
||||
}
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
@apply -mr-1;
|
||||
|
||||
&::before {
|
||||
@apply 2xl:-right-3;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
|
||||
button {
|
||||
.p-button-icon {
|
||||
@apply text-[#333333] text-xl;
|
||||
}
|
||||
|
||||
&:not(.p-button-icon-only) .p-button-icon {
|
||||
@apply ml-2;
|
||||
}
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#333333] text-lg font-medium font-vazir whitespace-nowrap no-underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(n + 7):not(:last-child) {
|
||||
@apply max-[1500px]:hidden;
|
||||
}
|
||||
|
||||
li:nth-child(n + 6):not(:last-child) {
|
||||
@apply max-[1380px]:hidden;
|
||||
}
|
||||
|
||||
li:nth-child(n + 5):not(:last-child) {
|
||||
@apply max-xl:hidden;
|
||||
}
|
||||
|
||||
li:not(:last-child:first-child) {
|
||||
@apply max-2xl:-mx-1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="header-cart-list-item">
|
||||
<span>{{ quantity }}x</span>
|
||||
<img :src="data.coverPath" />
|
||||
<div>
|
||||
<h3>{{ data.category[data.category.length - 1]?.name }}</h3>
|
||||
<h2>{{ data.title }}</h2>
|
||||
<span>
|
||||
<b>{{ numberFormat(data.specialPrice || data.price) }} </b>
|
||||
{{ $t('priceUnit') }}
|
||||
</span>
|
||||
</div>
|
||||
<Button icon="isax isax-trash" severity="secondary" text @click="$emit('remove', data.id)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps(['data', 'quantity'])
|
||||
|
||||
const numberFormat = (number) =>
|
||||
new Intl.NumberFormat('fa-IR', { maximumSignificantDigits: 3 }).format(number)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-cart-list-item {
|
||||
box-shadow: 0px 2px 12px #004b8226;
|
||||
@apply flex p-2 pl-6 w-[26.9rem] h-[10.1rem] bg-white rounded-lg shadow border relative;
|
||||
|
||||
&>span {
|
||||
@apply absolute left-5 top-4 text-xs font-poppins;
|
||||
}
|
||||
|
||||
img {
|
||||
@apply w-[9.2rem] h-[9.2rem] p-2;
|
||||
}
|
||||
|
||||
div {
|
||||
@apply w-max gap-1 flex flex-col py-4 font-vazir mr-6;
|
||||
|
||||
h3 {
|
||||
@apply text-right text-neutral-400 text-[0.9em] font-medium;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply w-[13.1rem] h-14 text-right text-zinc-800 text-[1.1em] font-bold line-clamp-2;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-zinc-800 text-[0.9em] mt-auto;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@apply w-10 h-10 mb-2 -mx-2 shrink-0 self-end text-2xl text-[#7F7F7F] rounded-full #{!important};
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="header-cart-list">
|
||||
<h3>{{ $t('itemsSubmittedInCart', { count: cart.count }) }}</h3>
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="({ product, quantity }, i) in items" :key="i">
|
||||
<AppDesktopHeaderCartItem :data="product" :quantity="quantity" @remove="remove" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Button :label="$t('showCart')" @click="show()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { popup } = inject('service')
|
||||
const cart = useCartStore()
|
||||
const items = computed(() => cart.items)
|
||||
|
||||
const emit = defineEmits(['hide'])
|
||||
const router = useRouter()
|
||||
|
||||
const show = () => {
|
||||
popup.value.hide()
|
||||
router.push('/checkout/cart')
|
||||
}
|
||||
const remove = async (id) => {
|
||||
await cart.update(id)
|
||||
if (cart.count < 1) popup.value.hide()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-cart-list {
|
||||
box-shadow: 0px 2px 10px 0px rgba(0, 75, 130, 0.16);
|
||||
@apply rtl w-[30.9rem] h-[40.6rem] flex flex-col gap-5 py-6 pl-2 bg-white rounded-2xl border overflow-hidden relative;
|
||||
|
||||
&>h3 {
|
||||
@apply text-sky-700 text-[0.9em] font-medium font-vazir mb-2.5 mr-8 ml-6;
|
||||
}
|
||||
|
||||
&>div {
|
||||
@apply overflow-y-auto h-[29.8rem];
|
||||
|
||||
ul {
|
||||
@apply w-full flex py-3 pr-8 pl-4 flex-col gap-3 items-center;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&>button {
|
||||
@apply bg-sky-700 border-sky-700 mr-8 ml-6 text-xl font-bold font-vazir #{!important};
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: ' ';
|
||||
box-shadow: 0px -3px 16px 0px rgba(7, 50, 115, 0.16);
|
||||
@apply absolute inset-x-0 -bottom-3 h-28 z-0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<ul class="drawer-menu">
|
||||
<li v-for="(item, i) in items" :key="i">
|
||||
<router-link :to="item.link" @click="sidebar.visible = false">
|
||||
<Button v-bind="item.props" iconPos="right" severity="secondary" text />
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps(['menu'])
|
||||
const { sidebar } = inject('service')
|
||||
|
||||
const items = ref([])
|
||||
items.value = props.menu.map((item) => {
|
||||
item.props = { label: item.title, icon: 'isax ' + item.icon }
|
||||
return item
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.drawer-menu {
|
||||
@apply rtl w-60 flex flex-col gap-3 px-4 pt-[1.4rem];
|
||||
|
||||
li {
|
||||
button {
|
||||
@apply h-10 py-1.5 justify-end;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-[#333333] text-2xl ml-3;
|
||||
}
|
||||
|
||||
.p-button-label {
|
||||
@apply text-[#333333] text-lg font-medium font-vazir grow-0 whitespace-nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<header class="header-mobile">
|
||||
<div>
|
||||
<Button v-if="h.back" v-bind="btns.back.props" @click="$router.go(-1)" />
|
||||
<Button v-if="h.menu" v-bind="btns.menu.props" @click="sidebar.visible = true" />
|
||||
<h2 v-if="h.title" v-text="h.title" />
|
||||
<AppMobileSearchBox v-if="h.search" />
|
||||
<span />
|
||||
<template v-for="(opt, i) in h.options" :key="i">
|
||||
<Button v-bind="opt.props" @click="opt.click" />
|
||||
</template>
|
||||
</div>
|
||||
<AppMobileToolbar v-if="h.toolbar" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { btns, navigation } = inject('mobile')
|
||||
const { sidebar } = inject('service')
|
||||
const { mobile } = inject('config')
|
||||
const h = computed(() => mobile.header)
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-mobile {
|
||||
box-shadow: 0px 2px 20px #004b8224;
|
||||
@apply w-full h-max bg-white shadow border-b pl-3 flex flex-col sticky top-0 z-10;
|
||||
|
||||
&>div {
|
||||
@apply h-[3.3rem] flex items-center;
|
||||
|
||||
&>button {
|
||||
@apply flex items-center text-[#292D32] shrink-0;
|
||||
|
||||
.p-button-icon {
|
||||
@apply text-2xl;
|
||||
}
|
||||
}
|
||||
|
||||
&>h2{
|
||||
@apply text-[#333333] text-sm font-medium font-vazir;
|
||||
}
|
||||
|
||||
&>span {
|
||||
@apply mr-auto;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<nav class="bottom-navigation">
|
||||
<ul>
|
||||
<li v-for="(item, i) in navigation" :key="i">
|
||||
<router-link :to="item.to" v-ripple>
|
||||
<Button v-bind="item.props" />
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { navigation } = inject('mobile');
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
Object.keys(navigation).forEach(key => {
|
||||
const item = navigation[key]
|
||||
if (item.to == route.path)
|
||||
item.props.icon += '5'
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.bottom-navigation {
|
||||
@apply fixed inset-x-0 bottom-0 h-16 pb-2 bg-white z-10;
|
||||
@apply shadow-[0px_-2px_5px_#0000000a] border-t border-[#F1F2F4];
|
||||
|
||||
ul {
|
||||
@apply w-full h-full flex justify-between items-center;
|
||||
|
||||
li {
|
||||
@apply flex justify-center flex-1;
|
||||
|
||||
a {
|
||||
button span {
|
||||
@apply text-[#7F7F7F] text-2xl;
|
||||
}
|
||||
|
||||
&.router-link-active button span {
|
||||
@apply text-primary-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="search-box" @click="active = true">
|
||||
<i class="isax isax-search-normal-1" />
|
||||
<template v-if="active">
|
||||
<InputText v-model="q" :placeholder="$t('searchIn')" autofocus @blur="active = false"
|
||||
@keypress.enter="search" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ $t("searchIn") }}</span>
|
||||
<img src="/images/logo-search.svg" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const active = ref(false);
|
||||
const q = ref(route.query.q || '');
|
||||
|
||||
const search = () => router.push({ path: '/search', query: { q: q.value } })
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search-box {
|
||||
@apply grow flex gap-1.5 items-center h-10 bg-[#F2F2F2] rounded-[0.4rem] px-3;
|
||||
|
||||
input {
|
||||
@apply h-full text-xs px-0 w-auto bg-transparent border-0 rtl focus:border-0 focus:shadow-none #{!important};
|
||||
}
|
||||
|
||||
i {
|
||||
@apply text-lg ml-1.5;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-[#5D5D5D] text-xs font-vazir;
|
||||
}
|
||||
|
||||
img {
|
||||
@apply w-24 h-5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="({ label, link }, i) in items" :key="i">
|
||||
<router-link :to="link">
|
||||
<span>{{ label }}</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const items = reactive([
|
||||
{ link: '/panel/seller/store', label: 'فروشگاه', icon: 'isax isax-shop' },
|
||||
{ link: '/panel/seller/products', label: 'همه کالاها', icon: 'isax isax-bag' },
|
||||
{ link: '/panel/seller/new-product', label: 'معرفی کالا', icon: 'isax isax-bag-cross' },
|
||||
{ link: '/panel/seller/orders', label: 'سفارشات', icon: 'isax isax-bag-tick-2' },
|
||||
])
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.header-mobile>ul {
|
||||
@apply w-full h-8 flex gap-6 px-6;
|
||||
|
||||
li {
|
||||
@apply translate-y-px;
|
||||
|
||||
a {
|
||||
|
||||
@apply h-full w-max text-[#333333] text-xs font-vazir;
|
||||
|
||||
&.router-link-active {
|
||||
@apply text-[#47B556] font-bold;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(.router-link-active) {
|
||||
@apply border-b-2 border-[#47B556];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user