create searchBox section

This commit is contained in:
HAM!DREZA
2024-06-10 13:18:04 +03:30
parent 5310ec4dbc
commit c8bfc3c177
22 changed files with 1124 additions and 596 deletions
+17
View File
@@ -2,6 +2,23 @@
<NuxtLoadingIndicator color="#ffbe33" :duration="1" :throttle="0"/> <NuxtLoadingIndicator color="#ffbe33" :duration="1" :throttle="0"/>
<NuxtLayout /> <NuxtLayout />
<div @click="showSearchBox = false">
<NuxtPage /> <NuxtPage />
</div>
<NuxtLayout name="footer"/> <NuxtLayout name="footer"/>
</template> </template>
<script setup>
const showSearchBox = useState("showSearchBox");
watch(showSearchBox, ()=>{
if (process.client) {
if (showSearchBox.value === true) {
document.body.classList.add('overflow-hidden')
}else{
document.body.classList.remove('overflow-hidden')
}
}
})
</script>
+2 -4
View File
@@ -92,7 +92,7 @@
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<NuxtLink :to="`${props.course?.id}`" <NuxtLink :to="`${props.course?._id}`"
class="font-[500] mt-[0.5vw] flex items-center justify-center gap-[0.417vw] md:h-[2.292vw] px-[4.103vw] md:px-0 py-[2.308vw] md:py-[0.521vw] bg-primaryGreen text-white text-[3.19vw] md:text-[0.833vw] rounded-[2.564vw] md:rounded-[0.521vw] md:w-[7.917vw]"> class="font-[500] mt-[0.5vw] flex items-center justify-center gap-[0.417vw] md:h-[2.292vw] px-[4.103vw] md:px-0 py-[2.308vw] md:py-[0.521vw] bg-primaryGreen text-white text-[3.19vw] md:text-[0.833vw] rounded-[2.564vw] md:rounded-[0.521vw] md:w-[7.917vw]">
شروع دوره شروع دوره
<svg <svg
@@ -129,7 +129,7 @@
</linearGradient> </linearGradient>
</defs> </defs>
</svg> </svg>
<span class="absolute top-[0.9vw] left-[0.9vw] text-[#383E43] font-bold thin text-[0.833vw]">{{props.course.satisfactionLevel}}</span> <span class="absolute top-[0.9vw] w-[1.5vw] text-center left-[0.4vw] text-[#383E43] font-bold thin text-[0.833vw]">{{props.course.averageRating}}</span>
</span> </span>
</div> </div>
@@ -141,9 +141,7 @@
<script setup> <script setup>
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
const props = defineProps(['course']); const props = defineProps(['course']);
const MyArray = ['Salam','Man','Sadegh','Hastam'];
const toast = useToast() const toast = useToast()
console.log(props);
function hideEndOfString(inputString) { function hideEndOfString(inputString) {
const words = inputString.split(' '); const words = inputString.split(' ');
if (words.length > 8) { if (words.length > 8) {
@@ -21,7 +21,8 @@
<!-- comment 1 --> <!-- comment 1 -->
<div <div
class="relative bg-[#F8F8F8] rounded-[3.846vw] md:rounded-[0.781vw] flex justify-between p-[4vw] md:p-[1.55vw]" v-for="item in props.props" :key="item.id"
class="mt-[1vw] relative bg-[#F8F8F8] rounded-[3.846vw] md:rounded-[0.781vw] flex justify-between p-[4vw] md:p-[1.55vw]"
> >
<img <img
src="/public/images/client1.png" src="/public/images/client1.png"
@@ -29,7 +30,7 @@
class="w-[10.256vw] md:w-[3.958vw] h-[10.256vw] md:h-[3.958vw] rounded-[50%]" class="w-[10.256vw] md:w-[3.958vw] h-[10.256vw] md:h-[3.958vw] rounded-[50%]"
/> />
<div class="ps-[3vw] md:ps-[1vw]"> <div class="ps-[3vw] md:ps-[1vw] w-full">
<p class="text-[3.59vw] md:text-[0.938vw] text-[#383E43]"> <p class="text-[3.59vw] md:text-[0.938vw] text-[#383E43]">
علی مصلحی علی مصلحی
</p> </p>
@@ -40,10 +41,8 @@
<p <p
class="w-[98%] text-[3.19vw] md:text-[0.833vw] text-[#383E43] leading-[4.785vw] md:leading-[1.25vw] mt-[2vw] md:mt-[0.5vw]" class="w-[98%] text-[3.19vw] md:text-[0.833vw] text-[#383E43] leading-[4.785vw] md:leading-[1.25vw] mt-[2vw] md:mt-[0.5vw]"
> >
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با {{ item.text }}
استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله
در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد
نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد
</p> </p>
</div> </div>
</div> </div>
@@ -73,7 +72,7 @@
</div> </div>
<!-- comment 2 --> <!-- comment 2 -->
<div <!-- <div
class="mt-[4vw] md:mt-[1vw] bg-[#F8F8F8] rounded-[3.846vw] md:rounded-[0.781vw]" class="mt-[4vw] md:mt-[1vw] bg-[#F8F8F8] rounded-[3.846vw] md:rounded-[0.781vw]"
> >
<div class="flex justify-between relative p-[4vw] md:p-[1.55vw]"> <div class="flex justify-between relative p-[4vw] md:p-[1.55vw]">
@@ -179,5 +178,10 @@
/> />
</svg> </svg>
</div> </div>
</div> </div> -->
</template> </template>
<script setup>
const props = defineProps(['props'])
</script>
@@ -61,8 +61,9 @@
class="mt-[3vw] md:mt-[0.7vw] h-[2.051vw] md:h-[0.521vw] rounded-[2vw] md:rounded-[0.521vw]" class="mt-[3vw] md:mt-[0.7vw] h-[2.051vw] md:h-[0.521vw] rounded-[2vw] md:rounded-[0.521vw]"
> >
<div <div
:class="`w-[${props.props.completionPercentage}%]`" :class="`w-[${props.props.completionPercentage}%]`"
class=" rounded-[2vw] md:rounded-[0.521vw] h-[2.051vw] md:h-[0.521vw]"
class="rounded-[2vw] md:rounded-[0.521vw] h-[2.051vw] md:h-[0.521vw]"
> >
<!-- {{props.props.completionPercentage}} --> <!-- {{props.props.completionPercentage}} -->
</div> </div>
@@ -74,6 +75,8 @@
<script setup> <script setup>
const adad = 2; const adad = 2;
const props = defineProps(['props']) const props = defineProps(['props'])
</script> </script>
<style scoped> <style scoped>
#progressbar { #progressbar {
@@ -84,7 +87,6 @@ const props = defineProps(['props'])
#progressbar > div { #progressbar > div {
background-color: #0d3f40; background-color: #0d3f40;
float: left; float: left;
left: 0; left: 0;
} }
</style> </style>
@@ -7,18 +7,16 @@
class="absolute h-[86.41vw] md:h-[40.625vw] top-0 left-0 w-full object-cover brightness-75" class="absolute h-[86.41vw] md:h-[40.625vw] top-0 left-0 w-full object-cover brightness-75"
/> />
<div class="mt-[14vw] ms-[9.5vw] z-[1] text-white"> <div class="mt-[14vw] ms-[9.5vw] z-[1] text-white">
<h1 <h1 class="text-[5.751vw] md:text-[1.69vw] font-[600] -tracking-[0.05vw]">
class="text-[5.751vw] md:text-[1.69vw] font-[600] -tracking-[0.05vw]"
>
{{ props.props.name }} {{ props.props.name }}
</h1> </h1>
<p class="text-[3.19vw] md:text-[0.833vw] mt-[0.6vw] text-[#E5E5E5]"> <p class="text-[3.19vw] md:text-[0.833vw] mt-[0.6vw] text-[#E5E5E5]">
{{props.props.categories[0].name}} {{ props.props.categories[0].name }}
</p> </p>
<p <p
class="hidden md:block w-[39.896vw] text-[1.055vw] leading-[1.582vw] mt-[1.2vw]" class="hidden md:block w-[39.896vw] text-[1.055vw] leading-[1.582vw] mt-[1.2vw]"
> >
{{props.props.description}} {{ props.props.description }}
</p> </p>
<p <p
class="text-[2.836vw] md:text-[0.741vw] text-[##E5E5E5] mt-[1.8vw] absolute bottom-[8vw] md:bottom-auto md:relative" class="text-[2.836vw] md:text-[0.741vw] text-[##E5E5E5] mt-[1.8vw] absolute bottom-[8vw] md:bottom-auto md:relative"
@@ -115,7 +113,9 @@
</svg> </svg>
<div class="space-y-[0.3vw]"> <div class="space-y-[0.3vw]">
<p class="text-[3.59vw] md:text-[1.055vw]">آخرین بروزرسانی</p> <p class="text-[3.59vw] md:text-[1.055vw]">آخرین بروزرسانی</p>
<p class="text-[2.518vw] md:text-[0.741vw]">{{props.props.createdAt}}</p> <p class="text-[2.518vw] md:text-[0.741vw]">
{{ shamsi[2] }} / {{ shamsi[1] }} / {{ shamsi[0] }}
</p>
</div> </div>
</div> </div>
<div class="border-l"></div> <div class="border-l"></div>
@@ -144,7 +144,9 @@
<div class="space-y-[0.3vw]"> <div class="space-y-[0.3vw]">
<p class="text-[3.59vw] md:text-[1.055vw]">مدت زمان دوره</p> <p class="text-[3.59vw] md:text-[1.055vw]">مدت زمان دوره</p>
<p class="text-[2.518vw] md:text-[0.741vw]">{{props.props.duration}} ساعت</p> <p class="text-[2.518vw] md:text-[0.741vw]">
{{ props.props.duration }} ساعت
</p>
</div> </div>
</div> </div>
<div class="border-l"></div> <div class="border-l"></div>
@@ -186,7 +188,9 @@
</svg> </svg>
<div class="space-y-[0.3vw]"> <div class="space-y-[0.3vw]">
<p class="text-[3.59vw] md:text-[1.055vw]">{{props.props.studentsCount}}</p> <p class="text-[3.59vw] md:text-[1.055vw]">
{{ props.props.studentsCount }}
</p>
<p class="text-[2.518vw] md:text-[0.741vw]">دانشجو</p> <p class="text-[2.518vw] md:text-[0.741vw]">دانشجو</p>
</div> </div>
</div> </div>
@@ -196,6 +200,16 @@
</template> </template>
<script setup> <script setup>
const props = defineProps(['props']) const props = defineProps(["props"]);
const isoDateString = props.props.createdAt;
const dateObject = new Date(isoDateString);
const year = dateObject.getFullYear();
const month = dateObject.getMonth() + 1;
const day = dateObject.getDate();
const hours = dateObject.getHours();
const minutes = dateObject.getMinutes();
const seconds = dateObject.getSeconds();
const shamsi = gregorian_to_jalali(year,month,day)
</script> </script>
@@ -19,17 +19,20 @@
</div> </div>
<div class="mt-[6vw] md:mt-[2.1vw] space-y-[3vw] md:space-y-[1vw] thin"> <div class="mt-[6vw] md:mt-[2.1vw] space-y-[3vw] md:space-y-[1vw] thin">
<div <div
class="px-[3vw] md:px-[0.4vw] flex items-center justify-between bg-[#F4FDFD] rounded-[3.846vw] md:rounded-[0.781vw] h-[15.385vw] md:h-[3.438vw] w-full smallShadow" v-for="headline in props.props.courseHeadlines" :key="headline.id"
class="px-[3vw] md:px-[0.4vw] flex items-center bg-[#F4FDFD] rounded-[3.846vw] md:rounded-[0.781vw] h-[15.385vw] md:h-[3.438vw] w-full smallShadow"
> >
<div class="flex items-center relative"> <div class="flex items-center relative basis-5/6 md:basis-3/6 w-full">
<span <span
class="borderb mx-[0.9vw] ps-[0.6vw] pe-[2.5vw] md:pe-[0.5vw] text-[4.038vw] md:text-[1.055vw] font-black text-[#878787]" class=" border-b-[0.15vw] border-[#878787] px-[1vw] md:px-[0.5vw] ms-[0.3vw] me-[2vw] md:me-[0.5vw] text-[4.038vw] md:text-[1.055vw] font-black text-[#878787]"
> >
1 {{ props.props.courseHeadlines.indexOf(headline)+1 }}
</span> </span>
<h2 class="border-r ps-[2.2vw]">HTML چیست؟</h2> <h2 class="border-r ps-[2.2vw]">{{ headline.headline }}</h2>
</div> </div>
<div class="hidden md:flex items-center text-[0.833vw] ms-[2.5vw]">
<div class="hidden md:flex items-center text-[0.833vw] w-full md:basis-2/6">
<span>08:55</span> <span>08:55</span>
<svg <svg
class="ms-[0.3vw] h-[1.042vw] w-[1.042vw]" class="ms-[0.3vw] h-[1.042vw] w-[1.042vw]"
@@ -54,97 +57,7 @@
</svg> </svg>
</div> </div>
<div class="flex gap-[1vw] pe-[1vw]"> <div class="flex gap-[1vw] basis-1/6 md:basis-1/6 w-full text-end justify-end md:pe-[2vw]">
<svg
class="h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9 11V17L11 15"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 17L7 15"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M22 10V15C22 20 20 22 15 22H9C4 22 2 20 2 15V9C2 4 4 2 9 2H14"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M22 10H18C15 10 14 9 14 6V2L22 10Z"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
class="h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 12V8.44002C4 4.02002 7.13 2.21002 10.96 4.42002L14.05 6.20002L17.14 7.98002C20.97 10.19 20.97 13.81 17.14 16.02L14.05 17.8L10.96 19.58C7.13 21.79 4 19.98 4 15.56V12Z"
stroke="#383E43"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
<div
class="px-[3vw] md:px-[0.4vw] flex items-center justify-between bg-[#F4FDFD] rounded-[3.846vw] md:rounded-[0.781vw] h-[15.385vw] md:h-[3.438vw] w-full smallShadow"
>
<div class="flex items-center relative">
<span
class="borderb mx-[0.9vw] ps-[0.6vw] pe-[2.5vw] md:pe-[0.5vw] text-[4.038vw] md:text-[1.055vw] font-black text-[#878787]"
>
1
</span>
<h2 class="border-r ps-[2.2vw]">HTML چیست؟</h2>
</div>
<div class="hidden md:flex items-center text-[0.833vw] ms-[2.5vw]">
<span>08:55</span>
<svg
class="ms-[0.3vw] h-[1.042vw] w-[1.042vw]"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18.3332 9.99996C18.3332 14.6 14.5998 18.3333 9.99984 18.3333C5.39984 18.3333 1.6665 14.6 1.6665 9.99996C1.6665 5.39996 5.39984 1.66663 9.99984 1.66663C14.5998 1.66663 18.3332 5.39996 18.3332 9.99996Z"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M13.0919 12.65L10.5086 11.1083C10.0586 10.8416 9.69189 10.2 9.69189 9.67497V6.2583"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<div class="flex gap-[1vw] pe-[1vw]">
<svg <svg
class="h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]" class="h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -199,106 +112,52 @@
</div> </div>
</div> </div>
<div
class="px-[3vw] md:px-[0.4vw] flex items-center justify-between bg-[#F4FDFD] rounded-[3.846vw] md:rounded-[0.781vw] h-[15.385vw] md:h-[3.438vw] w-full smallShadow"
>
<div class="flex items-center relative">
<span
class="borderb mx-[0.9vw] ps-[0.6vw] pe-[2.5vw] md:pe-[0.5vw] text-[4.038vw] md:text-[1.055vw] font-black text-[#878787]"
>
1
</span>
<h2 class="border-r ps-[2.2vw]">HTML چیست؟</h2>
</div>
<div class="hidden md:flex items-center text-[0.833vw] ms-[2.5vw]">
<span>08:55</span>
<svg
class="ms-[0.3vw] h-[1.042vw] w-[1.042vw]"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18.3332 9.99996C18.3332 14.6 14.5998 18.3333 9.99984 18.3333C5.39984 18.3333 1.6665 14.6 1.6665 9.99996C1.6665 5.39996 5.39984 1.66663 9.99984 1.66663C14.5998 1.66663 18.3332 5.39996 18.3332 9.99996Z"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M13.0919 12.65L10.5086 11.1083C10.0586 10.8416 9.69189 10.2 9.69189 9.67497V6.2583"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
<div class="flex gap-[1vw] pe-[1vw]">
<svg
class="h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9 11V17L11 15"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M9 17L7 15"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M22 10V15C22 20 20 22 15 22H9C4 22 2 20 2 15V9C2 4 4 2 9 2H14"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M22 10H18C15 10 14 9 14 6V2L22 10Z"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<svg
class="h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 12V8.44002C4 4.02002 7.13 2.21002 10.96 4.42002L14.05 6.20002L17.14 7.98002C20.97 10.19 20.97 13.81 17.14 16.02L14.05 17.8L10.96 19.58C7.13 21.79 4 19.98 4 15.56V12Z"
stroke="#383E43"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</div> </div>
</template> </template>
<script setup>
const props = defineProps(['props'])
</script>
<style scoped> <style scoped>
.borderb::before { .borderb::before {
content: ""; content: "";
position: absolute; position: absolute;
bottom: 0; bottom: 0;
right: 1.1vw; right: 1vw;
width: 1vw; width: 1.5vw;
background: #878787; background: #878787;
height: 0.208vw; height: 0.208vw;
border-radius: 20vw; border-radius: 20vw;
+130 -23
View File
@@ -76,7 +76,11 @@
<div <div
class="relative navigator flexBox gap-[2.2vw] text-[0.938vw] text-[#383E43] font-medium" class="relative navigator flexBox gap-[2.2vw] text-[0.938vw] text-[#383E43] font-medium"
> >
<div id="searchBox" class="hidden absolute right-0 w-full"> <div
v-if="showSearchBox"
id="searchBox"
class="absolute right-0 w-full"
>
<svg <svg
class="h-[1.406vw] w-[1.406vw] absolute top-[0.5vw] right-[1vw]" class="h-[1.406vw] w-[1.406vw] absolute top-[0.5vw] right-[1vw]"
viewBox="0 0 26 27" viewBox="0 0 26 27"
@@ -100,14 +104,21 @@
</svg> </svg>
<input <input
autofocus
id="searchElement"
v-model="search"
@click="logClick(true)"
@blur="logClick(false)"
type="text" type="text"
placeholder="نام آموزش، دوره و... را وارد کنید" placeholder="نام آموزش، دوره و... را وارد کنید"
class="thin w-full h-[2.604vw] rounded-[0.521vw] bg-[#F5F5F5] text-[#878787] text-[0.658vw] ps-[3vw]" class="thin w-full h-[2.604vw] rounded-[0.521vw] bg-[#F5F5F5] text-[#878787] text-[0.658vw] ps-[3vw]"
/> />
</div> </div>
<div
<div class="border-e pe-[0.8vw] cursor-pointer" @click="openSearchBox"> class="border-e pe-[0.8vw] cursor-pointer"
@click="openSearchBox"
>
<svg <svg
width="1.458vw" width="1.458vw"
height="1.406vw" height="1.406vw"
@@ -182,7 +193,8 @@
</span> </span>
</nuxt-link> </nuxt-link>
<nuxt-link to="/courses"> <nuxt-link to="/courses">
<span class="flexBox gap-[0.6vw]" <span
class="flexBox gap-[0.6vw]"
:class="{ 'text-primaryGreen': $route.path === '/courses' }" :class="{ 'text-primaryGreen': $route.path === '/courses' }"
> >
<svg <svg
@@ -219,11 +231,10 @@
</svg> </svg>
دوره های آموزشی دوره های آموزشی
</span> </span>
</nuxt-link> </nuxt-link>
<nuxt-link to="/about-us" <nuxt-link to="/about-us">
> <span
<span class="flexBox gap-[0.6vw]" class="flexBox gap-[0.6vw]"
:class="{ 'text-primaryGreen': $route.path === '/about-us' }" :class="{ 'text-primaryGreen': $route.path === '/about-us' }"
> >
<svg <svg
@@ -234,7 +245,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
:class="{ 'stroke-primaryGreen': $route.path === '/about-us' }" :class="{
'stroke-primaryGreen': $route.path === '/about-us',
}"
d="M12.5 22.5C18 22.5 22.5 18 22.5 12.5C22.5 7 18 2.5 12.5 2.5C7 2.5 2.5 7 2.5 12.5C2.5 18 7 22.5 12.5 22.5Z" d="M12.5 22.5C18 22.5 22.5 18 22.5 12.5C22.5 7 18 2.5 12.5 2.5C7 2.5 2.5 7 2.5 12.5C2.5 18 7 22.5 12.5 22.5Z"
stroke="#383E43" stroke="#383E43"
stroke-width="1.5" stroke-width="1.5"
@@ -242,7 +255,9 @@
stroke-linejoin="round" stroke-linejoin="round"
/> />
<path <path
:class="{ 'stroke-primaryGreen': $route.path === '/about-us' }" :class="{
'stroke-primaryGreen': $route.path === '/about-us',
}"
d="M12.5 8.5V13.5" d="M12.5 8.5V13.5"
stroke="#383E43" stroke="#383E43"
stroke-width="1.5" stroke-width="1.5"
@@ -250,7 +265,9 @@
stroke-linejoin="round" stroke-linejoin="round"
/> />
<path <path
:class="{ 'stroke-primaryGreen': $route.path === '/about-us' }" :class="{
'stroke-primaryGreen': $route.path === '/about-us',
}"
d="M12.4946 16.5H12.5036" d="M12.4946 16.5H12.5036"
stroke="#383E43" stroke="#383E43"
stroke-width="2" stroke-width="2"
@@ -266,7 +283,7 @@
<nuxt-link to="/account" v-if="authUser"> <nuxt-link to="/account" v-if="authUser">
<img <img
src="/images/Profile.png" :src="authUser?.avatarMedia"
alt="profile" alt="profile"
class="h-[3.698vw] w-[3.698vw] rounded-full p-[0.521vw]" class="h-[3.698vw] w-[3.698vw] rounded-full p-[0.521vw]"
/> />
@@ -274,28 +291,118 @@
<nuxt-link to="/login" v-else> <nuxt-link to="/login" v-else>
<svg <svg
class="h-[3.698vw] w-[3.698vw] rounded-full p-[0.521vw]" class="h-[3.698vw] w-[3.698vw] rounded-full p-[0.521vw]"
viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg"> viewBox="0 0 27 27"
<path opacity="0.4" d="M13.5 2.25C10.5525 2.25 8.15625 4.64625 8.15625 7.59375C8.15625 10.485 10.4175 12.825 13.365 12.9262C13.455 12.915 13.545 12.915 13.6125 12.9262C13.635 12.9262 13.6462 12.9262 13.6687 12.9262C13.68 12.9262 13.68 12.9262 13.6912 12.9262C16.5712 12.825 18.8325 10.485 18.8437 7.59375C18.8437 4.64625 16.4475 2.25 13.5 2.25Z" fill="#878787"/> fill="none"
<path d="M19.2151 15.9187C16.0763 13.8262 10.9576 13.8262 7.79633 15.9187C6.36758 16.875 5.58008 18.1687 5.58008 19.5525C5.58008 20.9362 6.36758 22.2187 7.78508 23.1637C9.36008 24.2212 11.4301 24.75 13.5001 24.75C15.5701 24.75 17.6401 24.2212 19.2151 23.1637C20.6326 22.2075 21.4201 20.925 21.4201 19.53C21.4088 18.1462 20.6326 16.8637 19.2151 15.9187Z" fill="#878787"/> xmlns="http://www.w3.org/2000/svg"
>
<path
opacity="0.4"
d="M13.5 2.25C10.5525 2.25 8.15625 4.64625 8.15625 7.59375C8.15625 10.485 10.4175 12.825 13.365 12.9262C13.455 12.915 13.545 12.915 13.6125 12.9262C13.635 12.9262 13.6462 12.9262 13.6687 12.9262C13.68 12.9262 13.68 12.9262 13.6912 12.9262C16.5712 12.825 18.8325 10.485 18.8437 7.59375C18.8437 4.64625 16.4475 2.25 13.5 2.25Z"
fill="#878787"
/>
<path
d="M19.2151 15.9187C16.0763 13.8262 10.9576 13.8262 7.79633 15.9187C6.36758 16.875 5.58008 18.1687 5.58008 19.5525C5.58008 20.9362 6.36758 22.2187 7.78508 23.1637C9.36008 24.2212 11.4301 24.75 13.5001 24.75C15.5701 24.75 17.6401 24.2212 19.2151 23.1637C20.6326 22.2075 21.4201 20.925 21.4201 19.53C21.4088 18.1462 20.6326 16.8637 19.2151 15.9187Z"
fill="#878787"
/>
</svg> </svg>
</nuxt-link> </nuxt-link>
</div> </div>
</nav> </nav>
<div
v-if="showSearchBox"
class="z-[10] w-[28vw] pt-[0.5vw] pb-[2vw] flex flex-col absolute mt-[8vw] rounded-[0.521vw] bg-[#F5F5F5] border shadow-md"
>
<span
class="text-[0.vw] w-full text-end pe-[0.8vw] cursor-pointer"
@click="closeSearchBox"
>بستن</span
>
<div class="flexBox mt-[1vw] text-[0.8vw]" v-if="search?.length < 3">
برای جستجو حداقل 3 کلمه وارد کنید...
</div>
<div v-else>
<p v-if="data.courses.length < 1" class="flexBox mt-[1vw] text-[0.8vw]">
دوره ای یافت نشد !!!
</p>
<div v-else class="cursor-pointer px-[1vw] pt-[2vw] space-y-[1.5vw]">
<div
@click="goToCourse(item?._id)"
v-for="item in data.courses"
:key="item.id"
class="flex justify-between items-center"
>
<div class="flex gap-[0.5vw]">
<img :src="item.image" alt="" class="h-[2.5vw] rounded-[0.2vw]" />
<div>
<p class="text-[0.8vw]">
{{ item.name }}
</p>
<p class="text-[0.6vw]">{{ item.categories[0].name }}</p>
</div>
</div>
<p class="text-[0.7vw]">مشاهده دوره</p>
</div>
</div>
</div>
<!-- <div v-for="item in data.courses" :key="item.id">{{ item.name }}</div> -->
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
const { authUser } = useAuth(); const { authUser } = useAuth();
const openSearchBox = () =>{ const showSearchBox = useState("showSearchBox", () => false);
const search = ref(null);
const openSearchBox = () => {
search.value = "";
showSearchBox.value = true;
if (process.client) { if (process.client) {
document.getElementById('searchBox').classList.add('!block') window.scrollTo({
top: 0,
behavior: "smooth",
});
} }
};
const router = useRouter();
const goToCourse = (val) =>{
router.push({
path: `/${val}`,
});
showSearchBox.value = false;
}
const closeSearchBox = () => {
showSearchBox.value = false;
if (process.client) {
document.body.classList.remove("overflow-hidden");
} }
onMounted(() => { };
})
const query = ref({});
const { data, refresh, pending } = await useFetch(() => "/api/course", {
method: "POST",
query: query,
});
watch(search, () => {
query.value = { name: search.value };
refresh();
console.log("data", data.value.courses.length);
//// console.log(search.value.length);
});
// watch(showSearchBox.value === false, () =>{
// // showSearchBox.value = null
// console.log('s');
// })
// function logClick(isInside) {
// if (isInside) {
// console.log("لاگ صحیح: داخل input کلیک شد.");
// } else {
// showSearchBox.value = false;
// }
// }
</script> </script>
+4 -1
View File
@@ -10,8 +10,9 @@ export default defineNuxtConfig({
'@nuxtjs/tailwindcss', '@nuxtjs/tailwindcss',
'@nuxt/image', '@nuxt/image',
'vue3-carousel-nuxt', 'vue3-carousel-nuxt',
"@formkit/nuxt" "@formkit/nuxt",
], ],
tailwindcss: { tailwindcss: {
cssPath: '~/assets/css/tailwind.css', cssPath: '~/assets/css/tailwind.css',
configPath: '~/tailwind.config.js', configPath: '~/tailwind.config.js',
@@ -19,5 +20,7 @@ export default defineNuxtConfig({
build: { build: {
transpile: ["vue-toastification"], transpile: ["vue-toastification"],
}, },
}) })
+7
View File
@@ -5,10 +5,17 @@
"scripts": { "scripts": {
"dev": "nuxt dev", "dev": "nuxt dev",
"build": "nuxt build", "build": "nuxt build",
"start": "PORT=5805 HOST=127.0.0.1 node .output/server/index.mjs",
"generate": "nuxt generate", "generate": "nuxt generate",
"preview": "nuxt preview", "preview": "nuxt preview",
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@formkit/nuxt": "^1.6.5", "@formkit/nuxt": "^1.6.5",
"@nuxt/image": "^1.7.0", "@nuxt/image": "^1.7.0",
+219 -8
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="flex items-center flex-col pb-[30vw] md:pb-[7vw]"> <div class="flex items-center flex-col pb-[30vw] md:pb-[7vw]">
<product-page-course-detailsHeader :props="data.course"/> <product-page-course-detailsHeader :props="data.course" />
<div <div
class="flex flex-col flex-col-reverse md:flex-row justify-between w-[87.69%] md:w-[81.04%] mt-[10vw] md:mt-[4.7vw]" class="flex flex-col flex-col-reverse md:flex-row justify-between w-[87.69%] md:w-[81.04%] mt-[10vw] md:mt-[4.7vw]"
> >
@@ -8,25 +8,236 @@
class="w-full md:w-[56.354vw] text-[#383E43] text-[3.59vw] md:text-[1.055vw] leading-[5.897vw] md:leading-[1.582vw]" class="w-full md:w-[56.354vw] text-[#383E43] text-[3.59vw] md:text-[1.055vw] leading-[5.897vw] md:leading-[1.582vw]"
> >
<product-page-course-detailsDescription :props="data.course" /> <product-page-course-detailsDescription :props="data.course" />
<product-page-course-detailsHeadlines :props="data.course"/> test
<product-page-course-detailsComments :props="data.course"/> <product-page-course-detailsHeadlines :props="data.course" />
<!-- <product-page-course-detailsComments :props="data.comments" /> -->
<div
class="flex items-center gap-[3vw] md:gap-[0.7vw] mt-[12vw] md:mt-[6.3vw] mb-[6vw] md:mb-[2.1vw]"
>
<svg
class="w-[1.538vw] md:w-[0.521vw] h-[8.462vw] md:h-[2.76vw]"
viewBox="0 0 10 53"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0 5C0 2.23858 2.23858 0 5 0H10V53H5C2.23858 53 0 50.7614 0 48V5Z"
fill="#0D3F40"
/>
</svg>
<span class="text-[4.544vw] md:text-[1.481vw] font-semibold"
>نظرات</span
>
</div>
<!-- comment 1 -->
<div
v-for="item in data.comments"
:key="item.id"
class="mt-[1vw] relative bg-[#F8F8F8] rounded-[3.846vw] md:rounded-[0.781vw] flex justify-between p-[4vw] md:p-[1.55vw]"
>
<img
src="/public/images/client1.png"
alt="profile"
class="w-[10.256vw] md:w-[3.958vw] h-[10.256vw] md:h-[3.958vw] rounded-[50%]"
/>
<div class="ps-[3vw] md:ps-[1vw] w-full">
<p class="text-[3.59vw] md:text-[0.938vw] text-[#383E43]">
علی مصلحی
</p>
<div class="thin">
<p class="text-[2.836vw] md:text-[0.741vw] text-[#878787]">
1402/09/14
</p>
<p
class="w-[98%] text-[3.19vw] md:text-[0.833vw] text-[#383E43] leading-[4.785vw] md:leading-[1.25vw] mt-[2vw] md:mt-[0.5vw]"
>
{{ item.text }}
</p>
</div>
<div
v-for="reply in item.replies"
:key="reply.id"
class="relative bg-[#F8F8F8] rounded-[3.846vw] md:rounded-[0.781vw] flex justify-between ps-[16vw] md:ps-[6vw] p-[2vw] md:p-[1.55vw]"
>
<img
src="/public/images/client3.png"
alt="profile"
class="w-[10.256vw] md:w-[3.958vw] h-[10.256vw] md:h-[3.958vw] rounded-[50%]"
/>
<div class="ps-[3vw] md:ps-[1vw] w-full">
<p class="text-[3.59vw] md:text-[0.938vw] text-[#383E43]">
علی مصلحی
</p>
<div class="thin">
<p class="text-[2.836vw] md:text-[0.741vw] text-[#878787]">
1402/09/14
</p>
<p
class="w-[98%] text-[3.19vw] md:text-[0.833vw] text-[#383E43] leading-[4.785vw] md:leading-[1.25vw] mt-[2vw] md:mt-[0.5vw]"
>
{{ reply.text }}
</p>
</div>
</div>
<!-- <svg
@click="sendReplay(reply.id)"
class="cursor-pointer absolute md:left-[2vw] left-[5vw] h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.12988 18.3101H15.1299C17.8899 18.3101 20.1299 16.0701 20.1299 13.3101C20.1299 10.5501 17.8899 8.31006 15.1299 8.31006H4.12988"
stroke="#383E43"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6.43012 10.8099L3.87012 8.24994L6.43012 5.68994"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg> -->
</div>
</div>
<svg
@click="sendReplay(item.id)"
class="cursor-pointer absolute md:left-[2vw] left-[5vw] h-[4.615vw] w-[4.615vw] md:h-[1.25vw] md:w-[1.25vw]"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.12988 18.3101H15.1299C17.8899 18.3101 20.1299 16.0701 20.1299 13.3101C20.1299 10.5501 17.8899 8.31006 15.1299 8.31006H4.12988"
stroke="#383E43"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6.43012 10.8099L3.87012 8.24994L6.43012 5.68994"
stroke="#383E43"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div> </div>
<div class="w-full md:w-[23.438vw] text-[#383E43]"> <div class="w-full md:w-[23.438vw] text-[#383E43]">
<product-page-course-detailsCourse-status :props="data.course"/> <product-page-course-detailsCourse-status :props="data.course" />
<product-page-course-detailsCourse-information :props="data.course"/> <product-page-course-detailsCourse-information :props="data.course" />
</div>
</div>
<div
class="w-[63.438vw] bg-[#F8F8F8] p-[4vw] md:p-[1vw] rounded-[3.846vw] md:rounded-[0.781vw] mt-[6vw] md:mt-[2vw]"
>
<div class="space-y-[5vw] md:space-y-[1vw]">
<div class="flex gap-[1vw]">
<img
src="/public/images/client1.png"
alt=""
class="h-[10.256vw0] md:h-[3.385vw] w-[10.256vw] md:w-[3.385vw] rounded-[50%]"
/>
<div
class="space-y-[4vw] mt-[1vw] md:mt-0 ms-[1vw] md:ms-0 md:space-y-0"
>
<p class="text-[3.59vw] md:text-[0.938vw]">علی مصلحی</p>
<p
v-if="replayTarget === null"
class="text-[2.836vw] md:text-[0.741vw] text-[#878787]"
>
پرسش جدید
</p>
<p v-else class="text-[2.836vw] md:text-[0.741vw] text-[#878787]">
پاسخ به علی مصلحی
</p>
</div>
</div>
<textarea
v-model="comment"
class="h-[50vw] md:h-[18vw] w-full rounded-[3.846vw] md:rounded-[0.781vw] p-[1vw]"
name=""
id=""
cols="30"
rows="10"
></textarea>
<div
class="flex justify-end gap-[1vw] text-[3.19vw] md:text-[0.833vw] text-white"
>
<button
@click="deleteComment"
class="h-[8.974vw] md:h-[2.292vw] w-[20.769vw] md:w-[5.573vw] rounded-[2.051vw] md:rounded-[0.521vw] bg-[#5A6268]"
>
حذف
</button>
<button
@click="sendComment"
class="cursor-pointer h-[8.974vw] md:h-[2.292vw] w-[20.769vw] md:w-[5.573vw] rounded-[2.051vw] md:rounded-[0.521vw] bg-primaryGreen"
>
ثبت
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { useToast } from "vue-toastification";
const toast = useToast();
const route = useRoute(); const route = useRoute();
const {data} = await useFetch("/api/productId", { const replayTarget = ref(null);
const { data, refresh } = await useFetch("/api/productId", {
method: "POST", method: "POST",
body: { body: {
id: route.params.product, id: route.params.product,
}, },
}) });
console.log(data.value.course); console.log(data.value.comments[0]);
const comment = ref(null);
const sendComment = async () => {
if (comment.value === "" || comment.value === null) {
return toast.error("متن خود را وارد کنید");
}
const comments = await useFetch("/api/comments/sendcomment", {
method: "POST",
query: {
id: data.value.course._id,
},
body: {
text: comment.value,
parent: replayTarget.value,
},
});
comment.value = null;
toast.success("نظر شما با موفقیت ثبت شد");
refresh();
};
const sendReplay = (id) => {
replayTarget.value = id;
};
const deleteComment = () => {
replayTarget.value = null;
comment.value = null;
};
</script> </script>
+63 -20
View File
@@ -1,11 +1,13 @@
<template> <template>
<div class="flex justify-between flex-col text-[#383E43]"> <div class="flex justify-between flex-col text-[#383E43]">
<div class="relative"> <div class="relative">
<img <img
src="/public/images/client3.png" :src="authUser?.avatarMedia"
class="w-[40.256vw] md:w-[8.177vw] h-[40.256vw] md:h-[8.177vw] rounded-[50%]" class="w-[40.256vw] md:w-[8.177vw] h-[40.256vw] md:h-[8.177vw] rounded-[50%]"
alt="" alt=""
/> />
<label for="file-upload" class="custom-file-upload">
<svg <svg
class="h-[10.769vw] md:h-[2.188vw] w-[10.769vw] md:w-[2.188vw] absolute -bottom-[0.2vw] right-[4vw] md:right-[0.7vw]" class="h-[10.769vw] md:h-[2.188vw] w-[10.769vw] md:w-[2.188vw] absolute -bottom-[0.2vw] right-[4vw] md:right-[0.7vw]"
viewBox="0 0 42 42" viewBox="0 0 42 42"
@@ -70,12 +72,27 @@
stroke-linejoin="round" stroke-linejoin="round"
/> />
</svg> </svg>
</label>
<input type="file" id="file-upload" @change="changePhoto" />
</div> </div>
<FormKit type="form" @submit="edit" :incomplete-message="false" :actions="false">
<div class="relative items-center gap-[5vw] md:gap-0 grid grid-cols-1 md:grid-cols-2 mt-[2vw] w-full md:w-[65%]">
<FormKit
type="form"
@submit="edit"
:incomplete-message="false"
:actions="false"
>
<div
class="relative items-center gap-[5vw] md:gap-0 grid grid-cols-1 md:grid-cols-2 mt-[2vw] w-full md:w-[65%]"
>
<div class="flex flex-col space-y-[0.2vw] mb-[0.5vw]"> <div class="flex flex-col space-y-[0.2vw] mb-[0.5vw]">
<label for="" class="text-[3.19vw] md:text-[0.833vw] font-[400]">شماره موبایل</label> <label for="" class="text-[3.19vw] md:text-[0.833vw] font-[400]"
<input type="text" name="name" >شماره موبایل</label
>
<input
type="text"
name="name"
:value="authUser?.phone" :value="authUser?.phone"
readonly readonly
class="bg-[#F5F5F5] h-[13.59vw] md:h-[2.969vw] w-full md:w-[19.01vw] rounded-[2.564vw] md:rounded-[0.521vw] px-[1vw]" class="bg-[#F5F5F5] h-[13.59vw] md:h-[2.969vw] w-full md:w-[19.01vw] rounded-[2.564vw] md:rounded-[0.521vw] px-[1vw]"
@@ -93,7 +110,9 @@
/> />
</div> </div>
<div class="flex flex-col space-y-[0.2vw]"> <div class="flex flex-col space-y-[0.2vw]">
<label for="" class="text-[3.19vw] md:text-[0.833vw] font-[400]">نام کاربری</label> <label for="" class="text-[3.19vw] md:text-[0.833vw] font-[400]"
>نام کاربری</label
>
<FormKit <FormKit
name="userName" name="userName"
:value="authUser?.userName" :value="authUser?.userName"
@@ -102,7 +121,9 @@
/> />
</div> </div>
<div class="flex flex-col space-y-[0.2vw]"> <div class="flex flex-col space-y-[0.2vw]">
<label for="" class="text-[3.19vw] md:text-[0.833vw] font-[400]">ایمیل</label> <label for="" class="text-[3.19vw] md:text-[0.833vw] font-[400]"
>ایمیل</label
>
<FormKit <FormKit
name="email" name="email"
:value="authUser?.email" :value="authUser?.email"
@@ -115,7 +136,8 @@
text-[3.59vw] md:text-[0.833vw] absolute md:left-[1vw] top-[80vw] md:top-[11vw] greenButton h-[13.59vw] text-[3.59vw] md:text-[0.833vw] absolute md:left-[1vw] top-[80vw] md:top-[11vw] greenButton h-[13.59vw]
md:h-[2.813vw] w-full md:w-[8.438vw] rounded-[2.564vw] md:rounded-[0.521vw] flexBox px-0 md:h-[2.813vw] w-full md:w-[8.438vw] rounded-[2.564vw] md:rounded-[0.521vw] flexBox px-0
" "
type="submit"> type="submit"
>
ویرایش ویرایش
</FormKit> </FormKit>
</div> </div>
@@ -125,24 +147,45 @@
<script setup> <script setup>
import { useToast } from 'vue-toastification' import { useToast } from "vue-toastification";
const toast = useToast() const toast = useToast();
const { authUser } = useAuth(); const { authUser } = useAuth();
const loading = ref(false) const loading = ref(false);
const edit = async (formData) => {
const edit = async(formData) => {
try { try {
loading.value = true; loading.value = true;
await $fetch('/api/user/update', { await $fetch("/api/user/update", {
method: 'PUT', method: "PUT",
body: formData body: formData,
}) });
location.reload() location.reload();
} catch (error) { } catch (error) {
toast.error(error) toast.error(error);
} finally { } finally {
loading.value = false; loading.value = false;
} }
};
const changePhoto = async (val) => {
const fd = new FormData('hi')
fd.append('name', 'hi')
console.log('fd', fd);
// const data = await useFetch("/api/uploader", {
// method: "POST",
// body: {
// file: val.target.value,
// directoryName: "profileAvatar",
// },
// });
// console.log("done", data);
};
</script>
<style scoped>
input[type="file"] {
display: none;
} }
</script> .custom-file-upload {
cursor: pointer;
}
</style>
+59 -13
View File
@@ -17,20 +17,28 @@
<div class="flex flex-col flex-col-reverse pb-[10vw]"> <div class="flex flex-col flex-col-reverse pb-[10vw]">
<ul <ul
class="space-y-[2vw] md:space-y-[0.5vw] mt-[0.5vw] border-b" class="space-y-[2vw] md:space-y-[0.5vw] mt-[0.5vw] border-b"
v-for="ticket in tickets" v-for="(ticket, index) in tickets"
:key="ticket.id" :key="ticket.id"
> >
<li class="flex justify-between items-center pt-[1.5vw] md:pt-0"> <li class="flex justify-between pt-[1.5vw] md:pt-0">
<p <p
@click="openTicket(ticket.id)" @click="openTicket(ticket.id)"
class="text-[3.59vw] md:text-[0.938vw] text-[#383E43]"> class="basis-2/6 text-[3.59vw] md:text-[0.938vw] text-[#383E43]">
{{ ticket.subject }} {{ ticket.subject }}
</p> </p>
<p class="text-[3.19vw] md:text-[0.833vw] text-[#878787]">
{{ ticket.createdAt }}
<p class="basis-2/6 flexBox relative text-[3.19vw] md:text-[0.833vw] text-[#878787]">
<span v-show="num === index"
v-for="(item, num) in jalali" :key="num"
>{{item[2]}} / {{item[1]}} / {{item[0]}}</span>
</p> </p>
<div class="flex items-center gap-[1vw]">
<div class="basis-2/6 flex items-center justify-end gap-[1vw]">
<button <button
@click="openTicket(ticket.id)" @click="openTicket(ticket.id)"
class="hidden md:block text-[0.741vw] bg-[#F5F5F5] text-[#878787] h-[2.135vw] w-[3.646vw] rounded-[0.521vw]" class="hidden md:block text-[0.741vw] bg-[#F5F5F5] text-[#878787] h-[2.135vw] w-[3.646vw] rounded-[0.521vw]"
@@ -52,7 +60,7 @@
<div class="flex flex-col flex-col-reverse"> <div class="flex flex-col flex-col-reverse">
<div v-if="loading" class="loadingText">درحال دریافت اطلاعات...</div> <div v-if="loading" class="loadingText">درحال دریافت اطلاعات...</div>
<div <div
class="justify-self-start mt-[5vw] md:mt-0" class="grid mt-[5vw] md:mt-0"
v-for="(message, index) in ticketMessages[0] v-for="(message, index) in ticketMessages[0]
?.ticketMessages" ?.ticketMessages"
:key="index" :key="index"
@@ -60,13 +68,20 @@
<div <div
class="rounded-b-[5.128vw] md:rounded-b-[1.042vw] rounded-l-[5.128vw] md:rounded-l-[1.042vw] w-[80vw] md:w-[40vw] p-[4vw] md:p-[2vw] bg-[#F8F8F8] mb-[1vw] text-[#383E43]" class="justify-self-start rounded-b-[5.128vw] md:rounded-b-[1.042vw] rounded-l-[5.128vw] md:rounded-l-[1.042vw] w-[80vw] md:w-[40vw] p-[4vw] md:p-[2vw] bg-[#F8F8F8] mb-[1vw] text-[#383E43]"
:class="{ 'justify-self-end rounded-b-[5.128vw] md:rounded-b-[1.042vw] rounded-l-[5.128vw] md:rounded-l-[1.042vw] w-[80vw] md:w-[40vw] p-[4vw] md:p-[2vw] !bg-[#F4FDFD] mb-[1vw] text-[#383E43]' : message.createdBy.roles[0] === 'Admin' }"
> >
<p class="text-[3.59vw] md:text-[1.055vw]"> <p class="text-[3.59vw] md:text-[1.055vw]">
{{ message.createdBy && "کاربر مهمان" }} {{ message.createdBy && "کاربر مهمان" }} {{ }}
</p> </p>
<p class="text-[#878787] text-[2.836vw] md:text-[0.741vw] mt-[0.5vw]"> <!-- <p class="text-[#878787] text-[2.836vw] md:text-[0.741vw] mt-[0.5vw]">
{{ message.createdAt }} {{ message.createdAt }}
</p> -->
<p class="text-[#878787] text-[2.836vw] md:text-[0.741vw] mt-[0.5vw]">
<span v-show="num === index"
v-for="(item, num) in jalali" :key="num"
>{{item[2]}} / {{item[1]}} / {{item[0]}}</span>
</p> </p>
<p class="text-[3.19vw] md:text-[0.833vw] mt-[5vw] md:mt-[2vw]"> <p class="text-[3.19vw] md:text-[0.833vw] mt-[5vw] md:mt-[2vw]">
{{ message.content }} {{ message.content }}
@@ -252,6 +267,7 @@
<p class="text-[3.59vw] md:text-[1.055vw]"> <p class="text-[3.59vw] md:text-[1.055vw]">
{{ message.createdBy && "کاربر مهمان" }} {{ message.createdBy && "کاربر مهمان" }}
</p> </p>
<p class="text-[#878787] text-[2.836vw] md:text-[0.741vw] mt-[0.5vw]"> <p class="text-[#878787] text-[2.836vw] md:text-[0.741vw] mt-[0.5vw]">
{{ message.createdAt }} {{ message.createdAt }}
</p> </p>
@@ -365,6 +381,35 @@ const subject = ref(null);
const content = ref(null); const content = ref(null);
const ticketMessages = ref([]); const ticketMessages = ref([]);
const update = ref(null) const update = ref(null)
const day = ref([])
const month = ref([])
const year = ref([])
const shamsi = ref([])
tickets.value.map(item =>{
item = new Date(item.createdAt)
year.value.push(item.getFullYear())
month.value.push(item.getMonth() + 1)
day.value.push(item.getDate())
})
const combinedItems = year.value.map((year, index) => ({
year,
month: month.value[index],
day: day.value[index],
}));
const values = combinedItems.map(item =>(
Object.values(item)
))
const jalali = ref([])
const shamsis = values.map(item => (
jalali.value.push(gregorian_to_jalali(item[0],item[1],item[2]))
))
const sendTicket = async () => { const sendTicket = async () => {
@@ -377,9 +422,10 @@ const sendTicket = async () => {
subject: subject.value, subject: subject.value,
}, },
}); });
load() // load()
addTicketForm.value = false; addTicketForm.value = false;
subject.value = null; subject.value = null;
location.reload()
}; };
@@ -390,7 +436,7 @@ const openTicket = async (data) => {
const slides = document.getElementsByClassName("close"); const slides = document.getElementsByClassName("close");
for (let i = 0; i < slides.length; i++) { for (let i = 0; i < slides.length; i++) {
if (slides[i].id === data) { if (slides[i].id === data) {
slides[i].style.maxHeight = (slides[i].scrollHeight * 2) + "px"; slides[i].style.maxHeight = slides[i].scrollHeight + "vh";
} else { } else {
slides[i].style.maxHeight = null; slides[i].style.maxHeight = null;
} }
@@ -406,7 +452,6 @@ const openTicket = async (data) => {
ticketMessages.value.push(single.value); ticketMessages.value.push(single.value);
reload() reload()
loading.value = false; loading.value = false;
}; };
const sendMessage = async (data, reload) => { const sendMessage = async (data, reload) => {
if (content.value === null || '') { if (content.value === null || '') {
@@ -424,6 +469,7 @@ const sendMessage = async (data, reload) => {
openTicket().reload() openTicket().reload()
}; };
const tab1 = `<p class="flex items-center gap-[0.5vw] p-[2vw] md:p-0 md:px-[0.6vw] rounded-[2.564vw]"> const tab1 = `<p class="flex items-center gap-[0.5vw] p-[2vw] md:p-0 md:px-[0.6vw] rounded-[2.564vw]">
<svg <svg
class="hidden md:block w-[1.406vw] h-[1.406vw]" class="hidden md:block w-[1.406vw] h-[1.406vw]"
+32 -3
View File
@@ -51,15 +51,15 @@
</svg> </svg>
</div> </div>
<p class="w-full text-end text-[4.544vw] md:text-[1.055vw]"> <p class="w-full text-end text-[4.544vw] md:text-[1.055vw]">
60,000 {{ data }},000
<span class="text-[2.836vw] md:text-[0.741vw] font-[400]"> <span class="text-[2.836vw] md:text-[0.741vw] font-[400]">
تومان تومان
</span> </span>
</p> </p>
<div class="pt-[10vw] md:pt-0 space-y-[3vw] md:space-y-[1vw]"> <div class="pt-[10vw] md:pt-0 space-y-[3vw] md:space-y-[1vw]">
<p class="text-[3.19vw] md:text-[0.833vw] text-[#383E43]">مبلغ مورد نظر</p> <p class="text-[3.19vw] md:text-[0.833vw] text-[#383E43]">مبلغ مورد نظر</p>
<input type="number" placeholder="تومان" class="text-[2.836vw] md:text-[0.741vw] h-[13.59vw] md:h-[2.969vw] w-full rounded-[2.564vw] md:rounded-[0.521vw] bg-[#F5F5F5] ps-[4vw] md:ps-[1vw]"> <input v-model="price" type="number" placeholder="تومان" class="text-[2.836vw] md:text-[0.741vw] h-[13.59vw] md:h-[2.969vw] w-full rounded-[2.564vw] md:rounded-[0.521vw] bg-[#F5F5F5] ps-[4vw] md:ps-[1vw]">
<button class="text-[3.59vw] md:text-[0.938vw] greenButton text-center h-[11.538vw] md:h-[2.969vw] w-full rounded-[2.564vw] md:rounded-[0.521vw]">شارژ حساب</button> <button @click="charge" class="text-[3.59vw] md:text-[0.938vw] greenButton text-center h-[11.538vw] md:h-[2.969vw] w-full rounded-[2.564vw] md:rounded-[0.521vw]">شارژ حساب</button>
</div> </div>
</div> </div>
</div> </div>
@@ -79,6 +79,35 @@
<script setup> <script setup>
import {Tabs, Tab} from 'vue3-tabs-component'; import {Tabs, Tab} from 'vue3-tabs-component';
const {data, refresh} = await useFetch('/api/user/balance')
const price = ref(null)
const charge = async () =>{
console.log(price.value);
const toPay = await useFetch("/api/user/add", {
method: "POST",
body: {
amount: price.value,
}
});
refresh()
price.value = null
console.log(toPay);
}
console.log('wallet,' , data.value);
const tab1 = const tab1 =
`<p `<p
class="flex items-center gap-[0.5vw] p-[2vw] md:p-0 md:px-[0.6vw] rounded-[2.564vw]"> class="flex items-center gap-[0.5vw] p-[2vw] md:p-0 md:px-[0.6vw] rounded-[2.564vw]">
+30 -4
View File
@@ -61,6 +61,8 @@
</svg> </svg>
<input <input
v-model="search"
@change="searchHandler"
class="thin w-[100%] md:w-[45vw] h-[10.769vw] md:h-[3.333vw] text-[2.836vw] md:text-[0.741vw] text-[#878787] rounded-[2.564vw] md:rounded-[0.781vw] ps-[12vw] md:ps-[3vw] md:ms-[1.563vw] shadow-sm" class="thin w-[100%] md:w-[45vw] h-[10.769vw] md:h-[3.333vw] text-[2.836vw] md:text-[0.741vw] text-[#878787] rounded-[2.564vw] md:rounded-[0.781vw] ps-[12vw] md:ps-[3vw] md:ms-[1.563vw] shadow-sm"
type="text" type="text"
placeholder="نام آموزش ، دوره و ... را وارد کنید" placeholder="نام آموزش ، دوره و ... را وارد کنید"
@@ -450,6 +452,7 @@ function prevPage(){
if (pageNumber.value > 1) { if (pageNumber.value > 1) {
pageNumber.value -= 1 pageNumber.value -= 1
query.value = { ...route.query, page: pageNumber }; query.value = { ...route.query, page: pageNumber };
router.push({ router.push({
path: "/courses", path: "/courses",
query: query.value, query: query.value,
@@ -482,12 +485,16 @@ function removeCategory(){
} }
if (query.value.hasOwnProperty("price")) { if (query.value.hasOwnProperty("price")) {
delete query.value.price; delete query.value.price;
}
if (query.value.hasOwnProperty("name")) {
delete query.value.name;
} }
router.push({ router.push({
path: "/courses", path: "/courses",
query: query.value, query: query.value,
}); });
pageNumber.value = 1 pageNumber.value = 1
search.value = null
refresh() refresh()
} }
@@ -499,6 +506,7 @@ const categories = categoryLis.data.value;
function selectCategory(id) { function selectCategory(id) {
query.value = { ...route.query, category: id }; query.value = { ...route.query, category: id };
if (query.value.hasOwnProperty("page")) { if (query.value.hasOwnProperty("page")) {
delete query.value.page; delete query.value.page;
} }
@@ -514,9 +522,9 @@ function selectCategory(id) {
}); });
pageNumber.value = 1 pageNumber.value = 1
refresh(); refresh();
watch(data , () => { // watch(data , () => {
paginationLength.value = Math.ceil(data.value.count / 2); // paginationLength.value = Math.ceil(data.value.count / 2);
}) // })
} }
@@ -565,8 +573,26 @@ const closeSortOptions = () => {
} }
}; };
const search = ref(null)
// watch(search, () =>{
// if (search.value.length > 2) {
// }
// })
const searchHandler = () =>{
query.value = { ...route.query, name: search.value };
if (query.value.hasOwnProperty("page")) {
delete query.value.page;
}
router.push({
path: "/courses",
query: query.value,
});
refresh();
}
watch(data , () => {
paginationLength.value = Math.ceil(data.value.count / 2);
})
</script> </script>
+7
View File
@@ -269,4 +269,11 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <Markdown :source="source" /> -->
</template> </template>
<script setup>
// import Markdown from 'vue3-markdown-it';
// const source = 'The *quick* brown fox, jumped **over** the lazy [dog](https://en.wikipedia.org/wiki/Dog)'
</script>
+25
View File
@@ -0,0 +1,25 @@
export default defineEventHandler(async (event) => {
const { public: { apiBase } } = useRuntimeConfig();
const token = getCookie(event, 'token');
const body = await readBody(event);
const query = getQuery(event);
console.log('c s',body);
console.log(query);
try {
const data = await $fetch(`${apiBase}/course/${query.id}/comment`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`
},
body
});
console.log('ser com', data);
return data;
} catch (error) {
return error;
}
})
+2 -2
View File
@@ -6,8 +6,9 @@ export default defineEventHandler(async (event) => {
let page = query.hasOwnProperty('page') ? `page=${query.page}` : ''; let page = query.hasOwnProperty('page') ? `page=${query.page}` : '';
// let sortBy = query.hasOwnProperty('sortBy') ? `price=${query.price}` : ''; // let sortBy = query.hasOwnProperty('sortBy') ? `price=${query.price}` : '';
let price = query.hasOwnProperty('price') ? `price=${query.price}` : ''; let price = query.hasOwnProperty('price') ? `price=${query.price}` : '';
let name = query.hasOwnProperty('name') ? `name=${query.name}` : '';
try { try {
const data = await $fetch(`${apiBase}/course?${category}&${page}&pageSize=2&${price}`, { const data = await $fetch(`${apiBase}/course?${category}&${page}&pageSize=2&${price}&${name}`, {
method: 'GET', method: 'GET',
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
@@ -17,7 +18,6 @@ export default defineEventHandler(async (event) => {
}); });
return data; return data;
} catch (error) { } catch (error) {
console.log('err :', error);
return error; return error;
} }
+24
View File
@@ -0,0 +1,24 @@
export default defineEventHandler(async (event) => {
const { public: { apiBase } } = useRuntimeConfig();
const query = getQuery(event);
// let page = query.hasOwnProperty('page') ? `page=${query.page}` : '';
// // let sortBy = query.hasOwnProperty('sortBy') ? `price=${query.price}` : '';
// let price = query.hasOwnProperty('price') ? `price=${query.price}` : '';
try {
const courseHeadline = await $fetch(`${apiBase}/courseHeadline?courseId=${query.id}`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'content-type': 'application/json'
}
});
console.log(courseHeadline);
return courseHeadline;
} catch (error) {
return error;
}
});
+34
View File
@@ -0,0 +1,34 @@
export default defineEventHandler(async (event) => {
const { public: { apiBase } } = useRuntimeConfig();
const token = getCookie(event, 'token');
const body = await readBody(event);
console.log(body);
try {
const data = await $fetch(`${apiBase}/uploader`, {
method: 'POST',
headers: {
// 'Accept': 'multipart/form-data',
'Content-Type': 'multipart/form-data',
'Authorization': `Bearer ${token}`
},
body: {
file: body.file,
directoryName: body.directoryName
}
});
console.log('uploader data', data);
return data;
} catch (error) {
console.log(error.data);
// if (error.statusCode == 401) {
// setCookie(event, 'token', '', {
// httpOnly: true,
// secure: true,
// maxAge: new Date(0),
// path: '/'
// })
// }
return error;
}
})
+21
View File
@@ -0,0 +1,21 @@
export default defineEventHandler(async (event) => {
const { public: { apiBase } } = useRuntimeConfig();
const token = getCookie(event, 'token');
const body = await readBody(event);
console.log(body);
try {
const data = await $fetch(`${apiBase}/user/add`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`
},
body
});
return data;
} catch (error) {
return error;
}
})
+19
View File
@@ -0,0 +1,19 @@
export default defineEventHandler(async (event) => {
const { public: { apiBase } } = useRuntimeConfig();
const token = getCookie(event, 'token');
try {
const data = await $fetch(`${apiBase}/user/balance`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${token}`
},
});
return data;
} catch (error) {
return error;
}
})
+32
View File
@@ -0,0 +1,32 @@
function gregorian_to_jalali(gy, gm, gd) {
var g_d_m, jy, jm, jd, gy2, days;
g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
gy2 = gm > 2 ? gy + 1 : gy;
days =
355666 +
365 * gy +
~~((gy2 + 3) / 4) -
~~((gy2 + 99) / 100) +
~~((gy2 + 399) / 400) +
gd +
g_d_m[gm - 1];
jy = -1595 + 33 * ~~(days / 12053);
days %= 12053;
jy += 4 * ~~(days / 1461);
days %= 1461;
if (days > 365) {
jy += ~~((days - 1) / 365);
days = (days - 1) % 365;
}
if (days < 186) {
jm = 1 + ~~(days / 31);
jd = 1 + (days % 31);
} else {
jm = 7 + ~~((days - 186) / 30);
jd = 1 + ((days - 186) % 30);
}
return [jy, jm, jd];
}
export {gregorian_to_jalali}