clean code
This commit is contained in:
Vendored
+5
@@ -10,6 +10,7 @@ declare module 'vue' {
|
||||
AddFaq: typeof import('./src/components/AddFaq.vue')['default']
|
||||
AddVideo: typeof import('./src/components/AddVideo.vue')['default']
|
||||
Avatar: typeof import('primevue/avatar')['default']
|
||||
Badge: typeof import('primevue/badge')['default']
|
||||
Button: typeof import('primevue/button')['default']
|
||||
Card: typeof import('primevue/card')['default']
|
||||
CategoryForm: typeof import('./src/components/CategoryForm.vue')['default']
|
||||
@@ -32,6 +33,7 @@ declare module 'vue' {
|
||||
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
|
||||
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
|
||||
InputGroup: typeof import('primevue/inputgroup')['default']
|
||||
InputNumber: typeof import('primevue/inputnumber')['default']
|
||||
InputText: typeof import('primevue/inputtext')['default']
|
||||
Listbox: typeof import('primevue/listbox')['default']
|
||||
Menu: typeof import('primevue/menu')['default']
|
||||
@@ -43,6 +45,7 @@ declare module 'vue' {
|
||||
PanelMenu: typeof import('primevue/panelmenu')['default']
|
||||
ProductDetails: typeof import('./src/components/ProductDetails.vue')['default']
|
||||
ProductForm: typeof import('./src/components/ProductForm.vue')['default']
|
||||
ProgressBar: typeof import('primevue/progressbar')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SecondStep: typeof import('./src/components/ProductFormSteps/SecondStep.vue')['default']
|
||||
@@ -53,7 +56,9 @@ declare module 'vue' {
|
||||
Textarea: typeof import('primevue/textarea')['default']
|
||||
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
|
||||
ThirdStep: typeof import('./src/components/ProductFormSteps/ThirdStep.vue')['default']
|
||||
ToggleButton: typeof import('primevue/togglebutton')['default']
|
||||
Toolbar: typeof import('primevue/toolbar')['default']
|
||||
TreeTable: typeof import('primevue/treetable')['default']
|
||||
UserDetails: typeof import('./src/components/UserDetails.vue')['default']
|
||||
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
|
||||
}
|
||||
|
||||
@@ -43,12 +43,8 @@ import { inject, reactive, ref } from "vue";
|
||||
import { useFaqStore } from "@/stores/faq";
|
||||
const store = useFaqStore();
|
||||
const saving = ref(false);
|
||||
|
||||
const dialog = inject("dialogRef");
|
||||
const type = ref();
|
||||
const produc = reactive(dialog.value.data);
|
||||
|
||||
const { toast, t } = inject("service");
|
||||
const { toast } = inject("service");
|
||||
const newFaq = reactive({
|
||||
title: "",
|
||||
description: "",
|
||||
|
||||
@@ -82,8 +82,6 @@
|
||||
|
||||
<script setup>
|
||||
import { inject, reactive, ref } from "vue";
|
||||
import { usePrimeVue } from "primevue/config";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useHeadlinesStore } from "@/stores/Headlines"
|
||||
import { useUploaderStore } from "@/stores/uploader";
|
||||
const loading = ref(false)
|
||||
@@ -95,7 +93,6 @@ const dialog = inject("dialogRef");
|
||||
const type = ref();
|
||||
const produc = reactive(dialog.value.data);
|
||||
const saving = ref(false)
|
||||
const videoStatus = ref(null);
|
||||
const setType = () => {
|
||||
if (type.value === "غیرفعال") {
|
||||
newEpisode.status = "DeActivate";
|
||||
@@ -134,53 +131,4 @@ const save = async() =>{
|
||||
saving.value = false;
|
||||
dialog.value.close()
|
||||
}
|
||||
|
||||
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
|
||||
removeFileCallback(index);
|
||||
totalSize.value -= parseInt(formatSize(file.size));
|
||||
totalSizePercent.value = totalSize.value / 10;
|
||||
|
||||
};
|
||||
|
||||
const onClearTemplatingUpload = (clear) => {
|
||||
clear();
|
||||
totalSize.value = 0;
|
||||
totalSizePercent.value = 0;
|
||||
};
|
||||
|
||||
const onSelectedFiles = (event) => {
|
||||
files.value = event.files;
|
||||
files.value.forEach((file) => {
|
||||
totalSize.value += parseInt(formatSize(file.size));
|
||||
});
|
||||
};
|
||||
|
||||
const uploadEvent = (callback) => {
|
||||
totalSizePercent.value = totalSize.value / 10;
|
||||
callback();
|
||||
};
|
||||
|
||||
const onTemplatedUpload = () => {
|
||||
toast.add({
|
||||
severity: "info",
|
||||
summary: "Success",
|
||||
detail: "File Uploaded",
|
||||
life: 3000,
|
||||
});
|
||||
};
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
const k = 1024;
|
||||
const dm = 3;
|
||||
const sizes = $primevue.config.locale.fileSizeTypes;
|
||||
|
||||
if (bytes === 0) {
|
||||
return `0 ${sizes[0]}`;
|
||||
}
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
|
||||
|
||||
return `${formattedSize} ${sizes[i]}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
+48
-102
@@ -1,107 +1,53 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-7 pt-5 w-96">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="name" class="w-full" />
|
||||
<label>{{ $t('name') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.name">{{ errors.name }}</small>
|
||||
</div>
|
||||
<div class="w-full flex gap-2 mt-4">
|
||||
<Button
|
||||
:label="$t('back')"
|
||||
outlined
|
||||
severity="secondary"
|
||||
class="ml-auto"
|
||||
@click="dialog.close()"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('save')"
|
||||
icon="pi pi-save"
|
||||
severity="success"
|
||||
@click="save()"
|
||||
:loading="saving"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<section class="flex flex-col gap-7 pt-5 w-96">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="name" class="w-full" />
|
||||
<label>{{ $t("name") }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.name">{{ errors.name }}</small>
|
||||
</div>
|
||||
<div class="w-full flex gap-2 mt-4">
|
||||
<Button
|
||||
:label="$t('back')"
|
||||
outlined
|
||||
severity="secondary"
|
||||
class="ml-auto"
|
||||
@click="dialog.close()"
|
||||
/>
|
||||
<Button
|
||||
:label="$t('save')"
|
||||
icon="pi pi-save"
|
||||
severity="success"
|
||||
@click="save()"
|
||||
:loading="saving"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useCategoriesStore } from '@/stores/categoreis'
|
||||
// import { useUploaderStore } from '@/stores/uploader';
|
||||
import { computed, inject, reactive, ref, watch } from 'vue'
|
||||
const name = ref('')
|
||||
const dialog = inject('dialogRef')
|
||||
|
||||
const { toast, t } = inject('service')
|
||||
|
||||
const { category } = dialog.value.data
|
||||
|
||||
const form = reactive(Object.assign({}, category || {}))
|
||||
|
||||
const errors = reactive({})
|
||||
|
||||
const file = ref({})
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
const store = useCategoriesStore()
|
||||
|
||||
const save = async () => {
|
||||
saving.value = true
|
||||
const { status, data } = await store.create(name)
|
||||
|
||||
saving.value = false
|
||||
|
||||
if (status === 201 || status === 200)
|
||||
toast.add({
|
||||
life: 2000,
|
||||
severity: 'success',
|
||||
summary: t('successful'),
|
||||
detail: t('دسته بندی جدید ایجاد شد')
|
||||
})
|
||||
dialog.value.close()
|
||||
|
||||
// form.headerImage = data.url
|
||||
// else
|
||||
// return toast.add({
|
||||
// life: 2000, severity: 'error', summary: t('error'), detail: data.msg
|
||||
// })
|
||||
|
||||
}
|
||||
// //Create or Update
|
||||
// const { status, data } =
|
||||
// !!category ? await store.update(category._id, form) : await store.store(form)
|
||||
|
||||
// saving.value = false
|
||||
|
||||
// if (status === 200 || status === 201) {
|
||||
// toast.add({
|
||||
// life: 2000,
|
||||
// severity: 'success',
|
||||
// summary: t('successful'),
|
||||
// detail: t(!!category ? 'updateSuccessfully' : 'storeSuccessfully')
|
||||
// })
|
||||
// dialog.value.close()
|
||||
// } else if (status === 422)
|
||||
// Object.assign(errors, data)
|
||||
// else
|
||||
// toast.add({
|
||||
// severity: 'error', life: 2000, detail: data.msg, summary: t('error')
|
||||
// })
|
||||
|
||||
// }
|
||||
|
||||
// watch(computed(() => Object.assign({}, form)), (value, old) => {
|
||||
// Object.keys(form).forEach((key) => {
|
||||
// if (value[key] != old[key]) delete errors[key]
|
||||
// })
|
||||
// })
|
||||
|
||||
// const parentCategoryID = computed({
|
||||
// get: () => form.parentCategoryID ? ({ [form.parentCategoryID]: true }) : null,
|
||||
// set: (v) => form.parentCategoryID = Object.keys(v)[0]
|
||||
// })
|
||||
</script>
|
||||
import { useCategoriesStore } from "@/stores/categoreis";
|
||||
import { inject, reactive, ref } from "vue";
|
||||
const name = ref("");
|
||||
const dialog = inject("dialogRef");
|
||||
const { toast, t } = inject("service");
|
||||
const errors = reactive({});
|
||||
const saving = ref(false);
|
||||
const store = useCategoriesStore();
|
||||
const save = async () => {
|
||||
saving.value = true;
|
||||
const { status, data } = await store.create(name);
|
||||
saving.value = false;
|
||||
if (status === 201 || status === 200)
|
||||
toast.add({
|
||||
life: 2000,
|
||||
severity: "success",
|
||||
summary: t("successful"),
|
||||
detail: t("دسته بندی جدید ایجاد شد"),
|
||||
});
|
||||
dialog.value.close();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -4,7 +4,6 @@
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('کد تخفیف') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmNumber" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ coupon.coupon.code }}</span>
|
||||
</div>
|
||||
</li>
|
||||
@@ -27,17 +26,16 @@
|
||||
<strong>{{ $t('تاریخ انقضا') }}</strong>
|
||||
<span>{{ jDate(coupon.coupon.endDate) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('نوع تخفیف') }}</strong>
|
||||
<span>{{ coupon.coupon.status === "Active" ? 'فعال' : 'غیرفعال' }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('ایجاد شده توسط') }}</strong>
|
||||
<span>{{ coupon.coupon.admin.name }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('وضعیت تخفیف') }}</strong>
|
||||
<span>{{ coupon.coupon.status === "Active" ? 'فعال' : 'غیرفعال' }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('تغییر وضعیت') }}</strong>
|
||||
<!-- <ToggleButton @change="changeStatus" v-model="checked" onLabel="فعال" offLabel="غیرفعال" /> -->
|
||||
<Tag v-if="coupon.coupon.status === 'DeActivate'" @click="changeToActive" class="cursor-pointer" severity="success" value="فعال سازی"></Tag>
|
||||
<Tag v-if="coupon.coupon.status === 'Active'" @click="changeToDeActive" class="cursor-pointer" severity="danger" value="غیرفعال سازی"></Tag>
|
||||
</li>
|
||||
@@ -50,15 +48,12 @@
|
||||
<script setup>
|
||||
import { inject, reactive } from 'vue';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
|
||||
import {useDiscountStore} from '../stores/discounts'
|
||||
const store = useDiscountStore()
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
const checked = ref(false);
|
||||
const { confirm, toast, t } = inject("service");
|
||||
const router = useRouter()
|
||||
const saving = ref(false)
|
||||
|
||||
const { toast, t } = inject("service");
|
||||
|
||||
const changeToDeActive = async() =>{
|
||||
const change = await store.changeStatus({
|
||||
id: coupon.coupon.id,
|
||||
@@ -107,11 +102,7 @@ const changeToActive = async() => {
|
||||
}
|
||||
|
||||
};
|
||||
const tabs = ref([
|
||||
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
|
||||
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
|
||||
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
|
||||
]);
|
||||
|
||||
const dialog = inject('dialogRef')
|
||||
|
||||
const coupon = reactive(Object.assign({}, dialog.value.data))
|
||||
|
||||
@@ -82,21 +82,17 @@
|
||||
|
||||
<script setup>
|
||||
import { inject, reactive, ref, watchEffect } from "vue";
|
||||
import { usePrimeVue } from "primevue/config";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useHeadlinesStore } from "@/stores/Headlines"
|
||||
import { useUploaderStore } from "@/stores/uploader";
|
||||
const loading = ref(false)
|
||||
const uploadStore = useUploaderStore();
|
||||
const saving = ref(false)
|
||||
|
||||
const store = useHeadlinesStore()
|
||||
const dialog = inject("dialogRef");
|
||||
const { toast, t } = inject("service");
|
||||
const type = ref();
|
||||
const produc = reactive(dialog.value.data);
|
||||
|
||||
|
||||
const videoStatus = ref(null);
|
||||
const setType = () => {
|
||||
if (type.value === "غیرفعال") {
|
||||
newEpisode.status = "DeActivate";
|
||||
@@ -150,54 +146,14 @@ const save = async() =>{
|
||||
console.log(send);
|
||||
saving.value = false
|
||||
dialog.value.close()
|
||||
toast.add({
|
||||
life: 2000,
|
||||
severity: "success",
|
||||
summary: t("successful"),
|
||||
detail: t("ویدیو آپدیت شد"),
|
||||
});
|
||||
}
|
||||
|
||||
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
|
||||
removeFileCallback(index);
|
||||
totalSize.value -= parseInt(formatSize(file.size));
|
||||
totalSizePercent.value = totalSize.value / 10;
|
||||
|
||||
};
|
||||
|
||||
const onClearTemplatingUpload = (clear) => {
|
||||
clear();
|
||||
totalSize.value = 0;
|
||||
totalSizePercent.value = 0;
|
||||
};
|
||||
|
||||
const onSelectedFiles = (event) => {
|
||||
files.value = event.files;
|
||||
files.value.forEach((file) => {
|
||||
totalSize.value += parseInt(formatSize(file.size));
|
||||
});
|
||||
};
|
||||
|
||||
const uploadEvent = (callback) => {
|
||||
totalSizePercent.value = totalSize.value / 10;
|
||||
callback();
|
||||
};
|
||||
|
||||
const onTemplatedUpload = () => {
|
||||
toast.add({
|
||||
severity: "info",
|
||||
summary: "Success",
|
||||
detail: "File Uploaded",
|
||||
life: 3000,
|
||||
});
|
||||
};
|
||||
|
||||
const formatSize = (bytes) => {
|
||||
const k = 1024;
|
||||
const dm = 3;
|
||||
const sizes = $primevue.config.locale.fileSizeTypes;
|
||||
|
||||
if (bytes === 0) {
|
||||
return `0 ${sizes[0]}`;
|
||||
}
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
|
||||
|
||||
return `${formattedSize} ${sizes[i]}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -6,17 +6,16 @@
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('id') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ user.product.id }}</span>
|
||||
<span>{{ faq.product.id }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('سوال') }}</strong>
|
||||
<span>{{ user.product.title }}</span>
|
||||
<span>{{ faq.product.title }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between gap-32">
|
||||
<strong>{{ $t('پاسخ') }}</strong>
|
||||
<span>{{ user.product.description }}</span>
|
||||
<span>{{ faq.product.description }}</span>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
@@ -26,22 +25,9 @@
|
||||
|
||||
<script setup>
|
||||
import { inject, reactive } from 'vue';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const tabs = ref([
|
||||
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
|
||||
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
|
||||
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
|
||||
]);
|
||||
const dialog = inject('dialogRef')
|
||||
|
||||
const user = reactive(Object.assign({}, dialog.value.data))
|
||||
|
||||
const confirmToSales = (value) => {
|
||||
dialog.value.data.confirm(user, value)
|
||||
}
|
||||
const faq = reactive(Object.assign({}, dialog.value.data))
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,91 +1,42 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-6 w-96">
|
||||
<ul class="flex flex-col gap-5">
|
||||
|
||||
<li class="flex items-center justify-between">
|
||||
<video :src="videoUrl.data.url" autoplay></video>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('عنوان') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ head.product.headline }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('توضیحات') }}</strong>
|
||||
<span>{{ head.product.description }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('تعداد نظرات') }}</strong>
|
||||
<span>{{ head.product.Comment.length }}</span>
|
||||
</li>
|
||||
<!--
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('createdAt') }}</strong>
|
||||
<span>{{ jDate(user.product.createdAt) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('updatedAt') }}</strong>
|
||||
<span>{{ jDate(user.product.updatedAt) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('زمان دوره') }}</strong>
|
||||
<span>{{ user.product.duration }} ساعت</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('زبان') }}</strong>
|
||||
<span>{{ user.product.language }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('price') }}</strong>
|
||||
<span>{{ numberFormat(user.product.price) }} تومان</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('تعداد دانشجو ها') }}</strong>
|
||||
<span>{{ user.product.studentsCount }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('مدرس دوره') }}</strong>
|
||||
<span>{{ user.product.teacher.name }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('حجم دوره') }}</strong>
|
||||
<span>{{ user.product.totalFileSize }} گیگابایت</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('امتیاز دانشجویان') }}</strong>
|
||||
<span>{{ user.product.averageRating }}</span>
|
||||
</li> -->
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="flex flex-col gap-6 w-96">
|
||||
<ul class="flex flex-col gap-5">
|
||||
<li class="flex items-center justify-between">
|
||||
<video :src="videoUrl.data.url" autoplay></video>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("عنوان") }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<span>{{ head.product.headline }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("توضیحات") }}</strong>
|
||||
<span>{{ head.product.description }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("تعداد نظرات") }}</strong>
|
||||
<span>{{ head.product.Comment.length }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, reactive, watchEffect } from 'vue';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
import { useUploaderStore } from "@/stores/uploader"
|
||||
import { inject, reactive, watchEffect } from "vue";
|
||||
import { useUploaderStore } from "@/stores/uploader";
|
||||
|
||||
import { ref } from 'vue';
|
||||
const store = useUploaderStore()
|
||||
const tabs = ref([
|
||||
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
|
||||
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
|
||||
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
|
||||
]);
|
||||
const dialog = inject('dialogRef')
|
||||
const videoUrl = ref()
|
||||
const head = reactive(Object.assign({}, dialog.value.data))
|
||||
console.log('head', );
|
||||
watchEffect(async() =>{
|
||||
videoUrl.value = await store.getUrl(head.product.videoUrl)
|
||||
console.log(videoUrl);
|
||||
})
|
||||
// const confirmToSales = (value) => {
|
||||
// dialog.value.data.confirm(user, value)
|
||||
// }
|
||||
import { ref } from "vue";
|
||||
const store = useUploaderStore();
|
||||
|
||||
const dialog = inject("dialogRef");
|
||||
const videoUrl = ref();
|
||||
const head = reactive(Object.assign({}, dialog.value.data));
|
||||
|
||||
watchEffect(async () => {
|
||||
videoUrl.value = await store.getUrl(head.product.videoUrl);
|
||||
console.log(videoUrl);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,44 +0,0 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
msg: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="greetings">
|
||||
<h1 class="green">{{ msg }}</h1>
|
||||
<h3>
|
||||
You’ve successfully created a project with
|
||||
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,88 +1,73 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-6 w-96">
|
||||
<div class="flex flex-col gap-2 items-center self-center">
|
||||
<Avatar :image="user.product.image" class="w-full h-40" />
|
||||
<h2 class="text-xl">{{ user.product.name }}</h2>
|
||||
<!-- <Tag :value="$t(user.accountType.toLowerCase())" severity="warning" /> -->
|
||||
<section class="flex flex-col gap-6 w-96">
|
||||
<div class="flex flex-col gap-2 items-center self-center">
|
||||
<Avatar :image="data.product.image" class="w-full h-40" />
|
||||
<h2 class="text-xl">{{ data.product.name }}</h2>
|
||||
</div>
|
||||
<ul class="flex flex-col gap-5">
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("category") }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<span>{{ data.product.categories[0].name }}</span>
|
||||
</div>
|
||||
<ul class="flex flex-col gap-5">
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('category') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmNumber" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ user.product.categories[0].name }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('id') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ user.product._id }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('تعداد ویدیو ها') }}</strong>
|
||||
<span>{{ user.product.countOfVideos }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('createdAt') }}</strong>
|
||||
<span>{{ jDate(user.product.createdAt) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('updatedAt') }}</strong>
|
||||
<span>{{ jDate(user.product.updatedAt) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('زمان دوره') }}</strong>
|
||||
<span>{{ user.product.duration }} ساعت</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('زبان') }}</strong>
|
||||
<span>{{ user.product.language }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('price') }}</strong>
|
||||
<span>{{ numberFormat(user.product.price) }} تومان</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('تعداد دانشجو ها') }}</strong>
|
||||
<span>{{ user.product.studentsCount }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('مدرس دوره') }}</strong>
|
||||
<span>{{ user.product.teacher.name }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('حجم دوره') }}</strong>
|
||||
<span>{{ user.product.totalFileSize }} گیگابایت</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('امتیاز دانشجویان') }}</strong>
|
||||
<span>{{ user.product.averageRating }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("id") }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<span>{{ data.product._id }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("تعداد ویدیو ها") }}</strong>
|
||||
<span>{{ data.product.countOfVideos }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("createdAt") }}</strong>
|
||||
<span>{{ jDate(data.product.createdAt) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("updatedAt") }}</strong>
|
||||
<span>{{ jDate(data.product.updatedAt) }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("زمان دوره") }}</strong>
|
||||
<span>{{ data.product.duration }} ساعت</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("زبان") }}</strong>
|
||||
<span>{{ data.product.language }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("price") }}</strong>
|
||||
<span>{{ numberFormat(data.product.price) }} تومان</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("تعداد دانشجو ها") }}</strong>
|
||||
<span>{{ data.product.studentsCount }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("مدرس دوره") }}</strong>
|
||||
<span>{{ data.product.teacher.name }}</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("حجم دوره") }}</strong>
|
||||
<span>{{ data.product.totalFileSize }} گیگابایت</span>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t("امتیاز دانشجویان") }}</strong>
|
||||
<span>{{ data.product.averageRating }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, reactive } from 'vue';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const tabs = ref([
|
||||
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
|
||||
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
|
||||
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
|
||||
]);
|
||||
const dialog = inject('dialogRef')
|
||||
import { inject, reactive } from "vue";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { numberFormat } from "@/utils/numberFormat";
|
||||
const dialog = inject("dialogRef");
|
||||
const data = reactive(Object.assign({}, dialog.value.data));
|
||||
|
||||
const user = reactive(Object.assign({}, dialog.value.data))
|
||||
|
||||
const confirmToSales = (value) => {
|
||||
dialog.value.data.confirm(user, value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,104 +0,0 @@
|
||||
<template>
|
||||
<section class="container w-full">
|
||||
<Stepper>
|
||||
<StepperPanel :header="$t('information')">
|
||||
<template #content="{ nextCallback }">
|
||||
<FirstStep @close="dialog.close()" @next="nextCallback" />
|
||||
</template>
|
||||
</StepperPanel>
|
||||
<StepperPanel :header="$t('details')">
|
||||
<template #content="{ nextCallback, prevCallback }">
|
||||
<SecondStep @prev="prevCallback" @next="nextCallback" />
|
||||
</template>
|
||||
</StepperPanel>
|
||||
<StepperPanel :header="$t('images')">
|
||||
<template #content="{ prevCallback }">
|
||||
<ThirdStep @prev="prevCallback" @save="save()" />
|
||||
</template>
|
||||
</StepperPanel>
|
||||
</Stepper>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useUploaderStore } from '@/stores/uploader';
|
||||
import FirstStep from './ProductFormSteps/FirstStep.vue';
|
||||
import SecondStep from './ProductFormSteps/SecondStep.vue';
|
||||
import ThirdStep from './ProductFormSteps/ThirdStep.vue';
|
||||
|
||||
import { inject, provide, reactive, ref } from 'vue';
|
||||
import { useProductsStore } from '@/stores/products';
|
||||
|
||||
const dialog = inject('dialogRef')
|
||||
const { toast, t } = inject('service')
|
||||
const { product } = dialog.value.data
|
||||
|
||||
const form = reactive(Object.assign({}, product || {}))
|
||||
|
||||
const errors = reactive({})
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
provide('data', { form, errors, saving })
|
||||
|
||||
const store = useProductsStore()
|
||||
|
||||
const uploader = useUploaderStore()
|
||||
|
||||
const save = async () => {
|
||||
saving.value = true
|
||||
|
||||
|
||||
//Upload File
|
||||
if (typeof form.coverImage === 'object') {
|
||||
const { status, data } = await uploader.single(form.coverImage)
|
||||
|
||||
if (status === 201)
|
||||
form.coverImage = data.url
|
||||
else
|
||||
return toast.add({
|
||||
life: 2000, severity: 'error', summary: t('error'), detail: data.msg
|
||||
})
|
||||
}
|
||||
|
||||
//upload images
|
||||
const files = form.images.filter((image) => typeof image === 'object')
|
||||
if (files.length > 0) {
|
||||
const { status, data } = await uploader.bulk(files)
|
||||
|
||||
if (status === 201)
|
||||
form.images = form.images.map((image) => {
|
||||
if (typeof image === 'object')
|
||||
return data.urls.shift();
|
||||
return image;
|
||||
})
|
||||
else
|
||||
return toast.add({
|
||||
life: 2000, severity: 'error', summary: t('error'), detail: data.msg
|
||||
})
|
||||
}
|
||||
|
||||
//Create or Update
|
||||
const { status, data } =
|
||||
!!product ? await store.update(product._id, form) : await store.store(form)
|
||||
|
||||
saving.value = false
|
||||
|
||||
if (status === 200 || status === 201) {
|
||||
toast.add({
|
||||
life: 2000,
|
||||
severity: 'success',
|
||||
summary: t('successful'),
|
||||
detail: t(!!category ? 'updateSuccessfully' : 'storeSuccessfully')
|
||||
})
|
||||
dialog.value.close()
|
||||
} else if (status === 422)
|
||||
Object.assign(errors, data)
|
||||
else
|
||||
toast.add({
|
||||
severity: 'error', life: 2000, detail: data.msg, summary: t('error')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-4 ">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FileUpload mode="advanced" accept="image/*" multiple :maxFileSize="10490000" @select="chooseImages"
|
||||
@remove="chooseImages" :invalidFileSizeMessage="$t('invalidFileSizeMessage')">
|
||||
<template #header="{ chooseCallback }">
|
||||
<div class="flex justify-between w-full items-center">
|
||||
<h2 class="font-bold text-xl ">{{ $t('imagesGallery') }}</h2>
|
||||
<Button :label="$t('chooseImage')" severity="secondary" icon="pi pi-images" text
|
||||
@click="chooseCallback()" />
|
||||
</div>
|
||||
</template>
|
||||
<template #content="{ files, removeFileCallback }">
|
||||
<div v-if="files.length > 0" class="flex gap-4 justify-between ">
|
||||
<div class="flex gap-4 w-96 h-96 relative m-4 rounded-lg overflow-hidden bg-gray-50 shrink-0">
|
||||
<img :src="form.coverImage?.objectURL || form.coverImage" class="object-cover">
|
||||
<div class=" bg-black/60 absolute bottom-2 right-2 flex rounded-full px-4 py-2">
|
||||
<span class="text-white text-sm m-auto"> {{ $t('mainImage') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Listbox v-model="form.coverImage" :options="files" @change="changeCoverImage(files)"
|
||||
class="border-none" listStyle="max-height:26rem">
|
||||
<template #option="{ option, index }">
|
||||
<div class="flex flex-row-reverse gap-4 items-center w-96 rounded">
|
||||
<img :src="option.objectURL || option"
|
||||
class="w-24 h-20 object-cover object-center rounded-lg">
|
||||
<div class="flex items-end justify-center h-full flex-col gap-4 ">
|
||||
<h3 class="font-bold truncate w-40 text-left">{{ option.name }}</h3>
|
||||
<span dir="ltr" class="text-sm">{{ formatSize(option.size) }}</span>
|
||||
</div>
|
||||
<Button icon="pi pi-times" size="small" text rounded severity="danger"
|
||||
class="ml-auto p-0 w-6 h-6" @click="removeFileCallback(index)" />
|
||||
</div>
|
||||
</template>
|
||||
</Listbox>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #empty>
|
||||
<div class="flex items-center justify-center flex-col gap-8 py-12 h-96">
|
||||
<i class="pi pi-images border-2 rounded-full p-5 text-8xl text-400 border-400" />
|
||||
<p>{{ $t('Drag and drop images to here to upload.') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</FileUpload>
|
||||
<small v-if="errors.coverImage || errors.images" class="p-error">
|
||||
{{ errors.coverImage || errors.images }}
|
||||
</small>
|
||||
</div>
|
||||
<div class="flex pt-4 justify-content-end">
|
||||
<Button :label="$t('back')" outlined severity="secondary" class="ml-auto" @click="$emit('close')" />
|
||||
<Button :label="$t('next')" icon="pi pi-arrow-left" iconPos="right" severity="success"
|
||||
@click="$emit('next')" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
const dialogRef = inject('dialogRef')
|
||||
|
||||
const { form, errors } = inject('data')
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-4 ">
|
||||
مرحله دوم
|
||||
<!-- <div class="flex gap-7 bg-gray-50/50 p-7 pt-7 border rounded-lg">
|
||||
<div class="flex flex-col gap-7">
|
||||
<div class="flex flex-col gap-1 w-96">
|
||||
<FloatLabel>
|
||||
<InputText v-model="form.title" class="w-full" />
|
||||
<label>{{ $t('title') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="form.titleEN" class="w-full ltr" />
|
||||
<label>{{ $t('titleEN') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.titleEN">{{ errors.titleEN }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="form.link" class="w-full ltr" />
|
||||
<label>{{ $t('link') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.link">{{ errors.link }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="form.metaTitle" class="w-full" />
|
||||
<label>{{ $t('metaTitle') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.metaTitle">{{ errors.metaTitle }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<Textarea v-model="form.metaDescription" rows="5" class="w-full" />
|
||||
<label>{{ $t('metaDescription') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.metaDescription">{{ errors.metaDescription }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1 gorw">
|
||||
<Editor v-model="form.description" :placeholder="$t('description')" class="h-full"
|
||||
editorStyle="max-height: calc(100% -46px)" />
|
||||
<small v-if="errors.description">{{ errors.description }}</small>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="flex pt-4 justify-content-end">
|
||||
<Button :label="$t('back')" outlined severity="secondary" class="ml-auto" @click="$emit('close')" />
|
||||
<Button :label="$t('next')" icon="pi pi-arrow-left" iconPos="right" severity="success"
|
||||
@click="$emit('next')" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
|
||||
const dialogRef = inject('dialogRef')
|
||||
|
||||
const { form, errors } = inject('data')
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,108 +0,0 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-4 ">
|
||||
<Panel :header="$t('اطلاعات دوره')">
|
||||
<div class="grid grid-cols-3 gap-7">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('title') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('مدت زمان دوره') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('دسته بندی') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('قیمت') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('مدرس') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('درصد تکمیل شده') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('سطح') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('زبان') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('تعداد ویدیو') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t('حجم کل دوره') }}</label>
|
||||
</FloatLabel>
|
||||
<small v-if="errors.title">{{ errors.title }}</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col gap-1 mt-8">
|
||||
<Editor :placeholder="$t('description')" class="h-full"
|
||||
editorStyle="min-height: 200px" />
|
||||
<small v-if="errors.description">{{ errors.description }}</small>
|
||||
</div>
|
||||
</Panel>
|
||||
<div class="flex pt-4 justify-content-end">
|
||||
<Button :label="$t('prev')" outlined severity="secondary" icon="pi pi-arrow-right" class="ml-auto"
|
||||
@click="$emit('prev')" />
|
||||
<Button :label="$t('save')" icon="pi pi-save" severity="success" :loading="saving" @click="$emit('save')" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { formatSize } from '@/utils/formatSize'
|
||||
import { inject } from 'vue';
|
||||
|
||||
const { form, errors, saving } = inject('data')
|
||||
|
||||
const chooseImages = ({ files }) => {
|
||||
if (!form.coverImage) form.coverImage = files?.[0] || null
|
||||
changeCoverImage(files)
|
||||
}
|
||||
|
||||
const changeCoverImage = (files) => {
|
||||
form.images = files.filter((img) => img != form.coverImage)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,88 +0,0 @@
|
||||
<script setup>
|
||||
import WelcomeItem from './WelcomeItem.vue'
|
||||
import DocumentationIcon from './icons/IconDocumentation.vue'
|
||||
import ToolingIcon from './icons/IconTooling.vue'
|
||||
import EcosystemIcon from './icons/IconEcosystem.vue'
|
||||
import CommunityIcon from './icons/IconCommunity.vue'
|
||||
import SupportIcon from './icons/IconSupport.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<DocumentationIcon />
|
||||
</template>
|
||||
<template #heading>Documentation</template>
|
||||
|
||||
Vue’s
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
|
||||
provides you with all information you need to get started.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<ToolingIcon />
|
||||
</template>
|
||||
<template #heading>Tooling</template>
|
||||
|
||||
This project is served and bundled with
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
|
||||
recommended IDE setup is
|
||||
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
|
||||
you need to test your components and web pages, check out
|
||||
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
|
||||
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
|
||||
>Cypress Component Testing</a
|
||||
>.
|
||||
|
||||
<br />
|
||||
|
||||
More instructions are available in <code>README.md</code>.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<EcosystemIcon />
|
||||
</template>
|
||||
<template #heading>Ecosystem</template>
|
||||
|
||||
Get official tools and libraries for your project:
|
||||
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
|
||||
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
|
||||
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
|
||||
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
|
||||
you need more resources, we suggest paying
|
||||
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
|
||||
a visit.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<CommunityIcon />
|
||||
</template>
|
||||
<template #heading>Community</template>
|
||||
|
||||
Got stuck? Ask your question on
|
||||
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
|
||||
Discord server, or
|
||||
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
|
||||
>StackOverflow</a
|
||||
>. You should also subscribe to
|
||||
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
|
||||
the official
|
||||
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
|
||||
twitter account for latest news in the Vue world.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<SupportIcon />
|
||||
</template>
|
||||
<template #heading>Support Vue</template>
|
||||
|
||||
As an independent project, Vue relies on community backing for its sustainability. You can help
|
||||
us by
|
||||
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
|
||||
</WelcomeItem>
|
||||
</template>
|
||||
@@ -3,20 +3,17 @@
|
||||
<div class="flex flex-col gap-2 items-center self-center">
|
||||
<Avatar :image="user.avatarMedia" shape="circle" class="w-24 h-24" />
|
||||
<h2 class="text-xl">{{ user.name }} {{ user.lastName }}</h2>
|
||||
<!-- <Tag :value="$t(user.accountType.toLowerCase())" severity="warning" /> -->
|
||||
</div>
|
||||
<ul class="flex flex-col gap-5">
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('phoneNumber') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmNumber" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ user.phone }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('id') }}</strong>
|
||||
<div class="flex items-center gap-2 w-max">
|
||||
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
|
||||
<span>{{ user.id }}</span>
|
||||
</div>
|
||||
</li>
|
||||
@@ -26,7 +23,6 @@
|
||||
</li>
|
||||
<li class="flex items-center justify-between">
|
||||
<strong>{{ $t('تغییر وضعیت') }}</strong>
|
||||
<!-- <ToggleButton @change="changeStatus" v-model="checked" onLabel="فعال" offLabel="غیرفعال" /> -->
|
||||
<Tag v-if="user.status === 'DeActivate'" @click="changeToActive" class="cursor-pointer" severity="success" value="فعال سازی"></Tag>
|
||||
<Tag v-if="user.status === 'Active'" @click="changeToDeActive" class="cursor-pointer" severity="danger" value="غیرفعال سازی"></Tag>
|
||||
</li>
|
||||
@@ -35,15 +31,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { jDate } from '@/utils/jDate';
|
||||
|
||||
import { inject, reactive } from 'vue';
|
||||
|
||||
const dialog = inject('dialogRef')
|
||||
import { useUsersStore } from '@/stores/users';
|
||||
const store = useUsersStore()
|
||||
const user = reactive(Object.assign({}, dialog.value.data.user))
|
||||
const { confirm, toast, t } = inject("service");
|
||||
const { toast, t } = inject("service");
|
||||
const confirmToSales = (value) => {
|
||||
dialog.value.data.confirm(user, value)
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<template>
|
||||
<div class="item">
|
||||
<i>
|
||||
<slot name="icon"></slot>
|
||||
</i>
|
||||
<div class="details">
|
||||
<h3>
|
||||
<slot name="heading"></slot>
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,19 +0,0 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
+3
-61
@@ -18,51 +18,13 @@
|
||||
|
||||
<script setup>
|
||||
import { getActivePinia } from 'pinia';
|
||||
import { inject, reactive, ref } from 'vue';
|
||||
import { inject, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const { t, confirm, toast } = inject('service')
|
||||
const { t, confirm } = inject('service')
|
||||
const router = useRouter()
|
||||
|
||||
const items = ref([
|
||||
|
||||
// {
|
||||
// label: t('blog'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// items: [
|
||||
// {
|
||||
// label: t('posts'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/blog/posts')
|
||||
// },
|
||||
// {
|
||||
// label: t('categories'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/blog/categories')
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// label: t('news'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/news')
|
||||
// },
|
||||
// {
|
||||
// label: t('emails'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/emails')
|
||||
// },
|
||||
// {
|
||||
// label: t('sms'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/sms')
|
||||
// },
|
||||
// {
|
||||
// label: t('moeens'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/moeens')
|
||||
// },
|
||||
|
||||
{
|
||||
label: t('pages'),
|
||||
icon: 'pi pi-sitemap',
|
||||
@@ -70,7 +32,7 @@ const items = ref([
|
||||
{
|
||||
label: t('header'),
|
||||
icon: 'pi pi-desktop',
|
||||
command: () => router.push('/pages/header-menu')
|
||||
command: () => router.push('/pages/main')
|
||||
},
|
||||
{
|
||||
label: t('aboutUs'),
|
||||
@@ -82,11 +44,6 @@ const items = ref([
|
||||
icon: 'pi pi-question-circle',
|
||||
command: () => router.push('/pages/Faq')
|
||||
},
|
||||
// {
|
||||
// label: t('contactUs'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/pages/contact-us')
|
||||
// }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -158,21 +115,6 @@ const items = ref([
|
||||
icon: 'pi pi-sitemap',
|
||||
command: () => router.push('/comments')
|
||||
},
|
||||
// {
|
||||
// label: t('orders'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/orders')
|
||||
// },
|
||||
// {
|
||||
// label: t('products'),
|
||||
// icon: 'pi pi-sitemap',
|
||||
// command: () => router.push('/products')
|
||||
// },
|
||||
// {
|
||||
// label: t('brands'),
|
||||
// icon: 'pi pi-apple',
|
||||
// command: () => router.push('/brands')
|
||||
// },
|
||||
|
||||
]);
|
||||
|
||||
|
||||
@@ -11,21 +11,5 @@ axios.interceptors.request.use((config) => {
|
||||
return config
|
||||
})
|
||||
|
||||
// axios.interceptors.response.use(
|
||||
// async (res) => {
|
||||
// if(!res.data?.url || !res.data?.urls){
|
||||
// res.data = JSON.parse(JSON.stringify(res.data).replaceAll('/uploads', baseURL + '/uploads'))
|
||||
// return res
|
||||
// }
|
||||
// },
|
||||
// async (error) => {
|
||||
// const status = error?.response?.status
|
||||
|
||||
// if (status === 401)
|
||||
// return window.location.href = '/login?to=' + window.location.href;
|
||||
|
||||
// return error?.response
|
||||
// }
|
||||
// )
|
||||
|
||||
export default axios
|
||||
|
||||
+84
-106
@@ -14,116 +14,16 @@ const router = createRouter({
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
// name: 'Main',
|
||||
redirect: '/courses',
|
||||
component: Main,
|
||||
children: [
|
||||
{
|
||||
path: '/categories',
|
||||
name: 'Categories',
|
||||
component: () => import('@/views/Categories.vue'),
|
||||
},
|
||||
{
|
||||
path: '/news',
|
||||
name: 'News',
|
||||
component: () => import('@/views/News.vue'),
|
||||
},
|
||||
{
|
||||
path: '/coupons',
|
||||
name: 'Coupons',
|
||||
component: () => import('@/views/Coupons.vue'),
|
||||
},
|
||||
{
|
||||
path: '/courses',
|
||||
name: 'courses',
|
||||
component: () => import('@/views/Courses.vue'),
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
name: 'create',
|
||||
component: () => import('@/views/Create.vue'),
|
||||
},
|
||||
// {
|
||||
// path: '/products',
|
||||
// name: 'Products',
|
||||
// component: () => import('@/views/Products.vue'),
|
||||
// },
|
||||
// {
|
||||
// path: '/orders',
|
||||
// name: 'Orders',
|
||||
// component: () => import('@/views/Orders.vue'),
|
||||
// },
|
||||
{
|
||||
path: '/comments',
|
||||
name: 'Comments',
|
||||
component: () => import('@/views/Comments.vue'),
|
||||
},
|
||||
{
|
||||
path: '/users',
|
||||
name: 'Users',
|
||||
component: () => import('@/views/Users.vue'),
|
||||
},
|
||||
{
|
||||
path: '/admins',
|
||||
name: 'admins',
|
||||
component: () => import('@/views/Admins.vue'),
|
||||
},
|
||||
{
|
||||
path: '/:id',
|
||||
name: 'Edit',
|
||||
component: () => import('@/views/Edit.vue'),
|
||||
},
|
||||
{
|
||||
path: '/CreateCoupon',
|
||||
name: 'CreateCoupon',
|
||||
component: () => import('@/views/CreateCoupon.vue'),
|
||||
},
|
||||
{
|
||||
path: '/coupon/:id',
|
||||
name: 'EditCoupon',
|
||||
component: () => import('@/views/EditCoupon.vue'),
|
||||
},
|
||||
{
|
||||
path: '/comments/:id',
|
||||
name: 'Answer',
|
||||
component: () => import('@/views/AnswerComment.vue'),
|
||||
},
|
||||
{
|
||||
path: '/tickets/:id',
|
||||
name: 'ShowTickets',
|
||||
component: () => import('@/views/ShowTickets.vue'),
|
||||
},
|
||||
{
|
||||
path: '/tickets',
|
||||
name: 'Tickets',
|
||||
component: () => import('@/views/Tickets.vue'),
|
||||
},
|
||||
{
|
||||
path: '/videos',
|
||||
name: 'Videos',
|
||||
component: () => import('@/views/Videos.vue'),
|
||||
},
|
||||
{
|
||||
path: '/videos/:id',
|
||||
name: 'AddVideos',
|
||||
component: () => import('@/views/AddVideos.vue'),
|
||||
},
|
||||
{
|
||||
path: '/couponsHistory',
|
||||
name: 'couponsHistory',
|
||||
component: () => import('@/views/couponsHistory.vue'),
|
||||
},
|
||||
// {
|
||||
// path: '/videos/create',
|
||||
// name: 'createVideo',
|
||||
// component: () => import('@/views/CreateVideo.vue'),
|
||||
// },
|
||||
{
|
||||
path: '/pages',
|
||||
redirect: '/pages/main',
|
||||
children: [
|
||||
{
|
||||
path: 'header-menu',
|
||||
name: 'HeaderMenu',
|
||||
path: 'main',
|
||||
name: 'main',
|
||||
component: () => import('@/views/HeaderMenu.vue')
|
||||
},
|
||||
{
|
||||
@@ -139,9 +39,87 @@ const router = createRouter({
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
path: '/courses',
|
||||
name: 'courses',
|
||||
component: () => import('@/views/Courses.vue'),
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
name: 'create',
|
||||
component: () => import('@/views/Create.vue'),
|
||||
},
|
||||
{
|
||||
path: '/videos',
|
||||
name: 'Videos',
|
||||
component: () => import('@/views/Videos.vue'),
|
||||
},
|
||||
{
|
||||
path: '/videos/:id',
|
||||
name: 'AddVideos',
|
||||
component: () => import('@/views/AddVideos.vue'),
|
||||
},
|
||||
{
|
||||
path: '/:id',
|
||||
name: 'Edit',
|
||||
component: () => import('@/views/Edit.vue'),
|
||||
},
|
||||
{
|
||||
path: '/coupons',
|
||||
name: 'Coupons',
|
||||
component: () => import('@/views/Coupons.vue'),
|
||||
},
|
||||
{
|
||||
path: '/CreateCoupon',
|
||||
name: 'CreateCoupon',
|
||||
component: () => import('@/views/CreateCoupon.vue'),
|
||||
},
|
||||
{
|
||||
path: '/coupon/:id',
|
||||
name: 'EditCoupon',
|
||||
component: () => import('@/views/EditCoupon.vue'),
|
||||
},
|
||||
{
|
||||
path: '/couponsHistory',
|
||||
name: 'couponsHistory',
|
||||
component: () => import('@/views/couponsHistory.vue'),
|
||||
},
|
||||
{
|
||||
path: '/categories',
|
||||
name: 'Categories',
|
||||
component: () => import('@/views/Categories.vue'),
|
||||
},
|
||||
{
|
||||
path: '/admins',
|
||||
name: 'admins',
|
||||
component: () => import('@/views/Admins.vue'),
|
||||
},
|
||||
{
|
||||
path: '/users',
|
||||
name: 'Users',
|
||||
component: () => import('@/views/Users.vue'),
|
||||
},
|
||||
{
|
||||
path: '/tickets',
|
||||
name: 'Tickets',
|
||||
component: () => import('@/views/Tickets.vue'),
|
||||
},
|
||||
{
|
||||
path: '/tickets/:id',
|
||||
name: 'ShowTickets',
|
||||
component: () => import('@/views/ShowTickets.vue'),
|
||||
},
|
||||
{
|
||||
path: '/comments',
|
||||
name: 'Comments',
|
||||
component: () => import('@/views/Comments.vue'),
|
||||
},
|
||||
{
|
||||
path: '/comments/:id',
|
||||
name: 'Answer',
|
||||
component: () => import('@/views/AnswerComment.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
@@ -13,32 +13,31 @@ export const useAdminsStore = defineStore('admins', {
|
||||
getters: {
|
||||
isLoggedIn: (state) => !!state.token
|
||||
},
|
||||
actions:{
|
||||
actions: {
|
||||
async login(number) {
|
||||
const { status, data, error } = await axios.post('/auth/login', {
|
||||
phone: number.value,
|
||||
type: 1
|
||||
})
|
||||
|
||||
|
||||
if (status == 200) {
|
||||
|
||||
|
||||
return { status, data, error }
|
||||
}
|
||||
|
||||
}, async sendOtp(number, code) {
|
||||
}, async sendOtp(number, code) {
|
||||
const { status, data, error } = await axios.post('/auth/login-step-two', {
|
||||
phone: number.value,
|
||||
code: code.value
|
||||
})
|
||||
console.log('data server', data);
|
||||
if (status == 200) {
|
||||
if (data.roles[0] === "Admin") {
|
||||
this.token = data.token
|
||||
}
|
||||
else{
|
||||
this.token = ''
|
||||
else {
|
||||
this.token = ''
|
||||
}
|
||||
|
||||
|
||||
return { status, data, error }
|
||||
}
|
||||
|
||||
|
||||
+17
-97
@@ -8,109 +8,29 @@ export const useCategoriesStore = defineStore('categories', {
|
||||
fetching: true,
|
||||
total: 0
|
||||
}),
|
||||
getters: {
|
||||
// treeSelect: (state) => (id = null) => {
|
||||
// if (id) {
|
||||
// const foreach = (items) => {
|
||||
// return items.filter((item) => {
|
||||
// if (item.key != id) {
|
||||
// if (item.children)
|
||||
// item.children = foreach(item.children)
|
||||
// return true
|
||||
// } else return false
|
||||
// })
|
||||
// }
|
||||
// return foreach(state.items)
|
||||
// }
|
||||
// return state.items
|
||||
// },
|
||||
find: (state) => (id) => {
|
||||
if (id) {
|
||||
const foreach = (items) => {
|
||||
const item = items.find(({ key }) => key == id)
|
||||
if (item) return item
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
if (item.children) {
|
||||
const temp = foreach(item.children)
|
||||
if (temp) return temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foreach(state.items)
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async index() {
|
||||
this.fetching = true
|
||||
const {data} = await axios.get('/courseCategory')
|
||||
const { data } = await axios.get('/courseCategory')
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
return data
|
||||
},
|
||||
async create(id) {
|
||||
const result = await axios.post('/courseCategory', {
|
||||
name: id.value
|
||||
})
|
||||
|
||||
async create(id) {
|
||||
const result = await axios.post('/courseCategory', {
|
||||
name: id.value
|
||||
})
|
||||
return result;
|
||||
},
|
||||
async remove(id) {
|
||||
const result = await axios.delete(`/courseCategory/${id}`)
|
||||
|
||||
|
||||
return result;
|
||||
},
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items.splice(index, 1)
|
||||
}
|
||||
|
||||
async store(form) {
|
||||
const result = await axios.post('/admin/categories', form)
|
||||
|
||||
if (result.status === 201)
|
||||
this.items.unshift(result.data)
|
||||
|
||||
return result;
|
||||
},
|
||||
async update(id, form) {
|
||||
const result = await axios.put(`/admin/categories/${id}`, form)
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items[index] = result.data
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async remove(id) {
|
||||
const result = await axios.delete(`/courseCategory/${id}`)
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items.splice(index, 1)
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
return result;
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,12 +0,0 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
@@ -8,133 +8,26 @@ export const useCommentStore = defineStore('comments', {
|
||||
fetching: true,
|
||||
total: 0
|
||||
}),
|
||||
getters: {
|
||||
// treeSelect: (state) => (id = null) => {
|
||||
// if (id) {
|
||||
// const foreach = (items) => {
|
||||
// return items.filter((item) => {
|
||||
// if (item.key != id) {
|
||||
// if (item.children)
|
||||
// item.children = foreach(item.children)
|
||||
// return true
|
||||
// } else return false
|
||||
// })
|
||||
// }
|
||||
// return foreach(state.items)
|
||||
// }
|
||||
// return state.items
|
||||
// },
|
||||
find: (state) => (id) => {
|
||||
if (id) {
|
||||
const foreach = (items) => {
|
||||
const item = items.find(({ key }) => key == id)
|
||||
if (item) return item
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
if (item.children) {
|
||||
const temp = foreach(item.children)
|
||||
if (temp) return temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foreach(state.items)
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async reply(id, body) {
|
||||
this.fetching = true
|
||||
const data = await axios.post(`/course/${id}/comment`, body)
|
||||
this.fetching = false
|
||||
|
||||
return data
|
||||
|
||||
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async ticket(id) {
|
||||
this.fetching = true
|
||||
const { status, data } = await axios.get(`/ticket/${id}`)
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
},
|
||||
async close(id){
|
||||
this.fetching = true
|
||||
const { status, data } = await axios.patch(`/ticket/close/${id}`, id)
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
},
|
||||
|
||||
async create(id) {
|
||||
|
||||
@@ -22,20 +22,6 @@ export const useDiscountStore = defineStore('discounts', {
|
||||
this.fetching = false
|
||||
return data
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async index() {
|
||||
this.items = [];
|
||||
this.fetching = true
|
||||
@@ -63,65 +49,6 @@ export const useDiscountStore = defineStore('discounts', {
|
||||
this.fetching = false
|
||||
return data
|
||||
}
|
||||
// async create(newCourse) {
|
||||
// const { status, data } = await axios.post('/course', newCourse)
|
||||
|
||||
// return data
|
||||
|
||||
// },
|
||||
// async delete(id) {
|
||||
// const { status, data } = await axios.delete(`/course/${id}`, id)
|
||||
|
||||
// return data
|
||||
|
||||
// },
|
||||
|
||||
// async edit(id) {
|
||||
// const { status, data } = await axios.get(`/course/${id}`)
|
||||
// return data
|
||||
|
||||
// },
|
||||
|
||||
|
||||
|
||||
// async store(form) {
|
||||
// const result = await axios.post('/admin/products/add', form)
|
||||
|
||||
// if (result.status === 201)
|
||||
// this.items.unshift(result.data)
|
||||
|
||||
// return result;
|
||||
// },
|
||||
// async update(id, form) {
|
||||
// const result = await axios.put(`/admin/products/${id}`, form)
|
||||
|
||||
// if (result.status === 200) {
|
||||
// const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
// this.items[index] = result.data
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// },
|
||||
// async confirm(id, form) {
|
||||
// const result = await axios.post(`/admin/products/confirm/${id}`, form)
|
||||
|
||||
// if (result.status === 200) {
|
||||
// const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
// this.items[index] = result.data
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// },
|
||||
// async remove(id) {
|
||||
// const result = await axios.delete(`/admin/products/${id}`)
|
||||
|
||||
// if (result.status === 200) {
|
||||
// const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
// this.items.splice(index, 1)
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// },
|
||||
|
||||
}
|
||||
})
|
||||
@@ -24,26 +24,5 @@ export const useFaqStore = defineStore('faq', {
|
||||
const data = await axios.post('/admins/faq/category', form)
|
||||
return data
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async confirm(item, value) {
|
||||
item.confirming = true
|
||||
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
|
||||
item.confirming = false
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == item.id)
|
||||
if (index >= 0)
|
||||
this.items.splice(index, 1)
|
||||
else
|
||||
this.items.unshift(item)
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
}
|
||||
})
|
||||
+3
-42
@@ -20,7 +20,9 @@ export const useProductsStore = defineStore('products', {
|
||||
const { status, data } = await axios.get('/course', { params })
|
||||
|
||||
this.fetching = false
|
||||
return this.items = data.courses
|
||||
|
||||
this.items = data
|
||||
|
||||
|
||||
},
|
||||
|
||||
@@ -48,46 +50,5 @@ export const useProductsStore = defineStore('products', {
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
// async store(form) {
|
||||
// const result = await axios.post('/admin/products/add', form)
|
||||
|
||||
// if (result.status === 201)
|
||||
// this.items.unshift(result.data)
|
||||
|
||||
// return result;
|
||||
// },
|
||||
// async update(id, form) {
|
||||
// const result = await axios.put(`/admin/products/${id}`, form)
|
||||
|
||||
// if (result.status === 200) {
|
||||
// const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
// this.items[index] = result.data
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// },
|
||||
// async confirm(id, form) {
|
||||
// const result = await axios.post(`/admin/products/confirm/${id}`, form)
|
||||
|
||||
// if (result.status === 200) {
|
||||
// const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
// this.items[index] = result.data
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// },
|
||||
// async remove(id) {
|
||||
// const result = await axios.delete(`/admin/products/${id}`)
|
||||
|
||||
// if (result.status === 200) {
|
||||
// const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
// this.items.splice(index, 1)
|
||||
// }
|
||||
|
||||
// return result;
|
||||
// },
|
||||
|
||||
}
|
||||
})
|
||||
+1
-41
@@ -23,47 +23,7 @@ export const useStaticsStore = defineStore('statics', {
|
||||
const result = await axios.patch('/admin/settings/edit', data)
|
||||
this.fetching = false
|
||||
return result
|
||||
// if (status === 200) {
|
||||
// this.items = data
|
||||
// // this.total = data.total
|
||||
// }
|
||||
},
|
||||
async create(id) {
|
||||
const result = await axios.post('/courseCategory', {
|
||||
name: id.value
|
||||
})
|
||||
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
async store(form) {
|
||||
const result = await axios.post('/admin/categories', form)
|
||||
|
||||
if (result.status === 201)
|
||||
this.items.unshift(result.data)
|
||||
|
||||
return result;
|
||||
},
|
||||
async update(id, form) {
|
||||
const result = await axios.put(`/admin/categories/${id}`, form)
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items[index] = result.data
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async remove(id) {
|
||||
const result = await axios.delete(`/courseCategory/${id}`)
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items.splice(index, 1)
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
}
|
||||
})
|
||||
+4
-170
@@ -8,200 +8,34 @@ export const useTicketsStore = defineStore('tickets', {
|
||||
fetching: true,
|
||||
total: 0
|
||||
}),
|
||||
getters: {
|
||||
// treeSelect: (state) => (id = null) => {
|
||||
// if (id) {
|
||||
// const foreach = (items) => {
|
||||
// return items.filter((item) => {
|
||||
// if (item.key != id) {
|
||||
// if (item.children)
|
||||
// item.children = foreach(item.children)
|
||||
// return true
|
||||
// } else return false
|
||||
// })
|
||||
// }
|
||||
// return foreach(state.items)
|
||||
// }
|
||||
// return state.items
|
||||
// },
|
||||
find: (state) => (id) => {
|
||||
if (id) {
|
||||
const foreach = (items) => {
|
||||
const item = items.find(({ key }) => key == id)
|
||||
if (item) return item
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
if (item.children) {
|
||||
const temp = foreach(item.children)
|
||||
if (temp) return temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foreach(state.items)
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async index(page, rows) {
|
||||
this.fetching = true
|
||||
|
||||
const params = { page, rows }
|
||||
|
||||
const { status, data } = await axios.get('/ticket', { params })
|
||||
|
||||
const { status, data } = await axios.get('/ticket')
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
},
|
||||
async ticket(id) {
|
||||
this.fetching = true
|
||||
const { status, data } = await axios.get(`/ticket/${id}`)
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
async close(id){
|
||||
this.fetching = true
|
||||
const { status, data } = await axios.patch(`/ticket/close/${id}`, id)
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
},
|
||||
async reply(form){
|
||||
this.fetching = true
|
||||
const { status, data } = await axios.post('/ticket/message', form)
|
||||
this.fetching = false
|
||||
return data
|
||||
if (status === 200) {
|
||||
const map = (items, parent = null) => {
|
||||
return items.map((item) => {
|
||||
const temp = {
|
||||
key: item._id,
|
||||
label: item.name,
|
||||
data: item
|
||||
}
|
||||
if (parent)
|
||||
temp.data.parentCategoryID = { [parent]: true }
|
||||
if (item.sub) {
|
||||
temp.children = map(item.sub, temp.key)
|
||||
delete temp.data.sub
|
||||
}
|
||||
return temp;
|
||||
});
|
||||
}
|
||||
|
||||
this.items = map(data)
|
||||
|
||||
// this.items = data.items
|
||||
// this.total = data.total
|
||||
}
|
||||
},
|
||||
async create(id) {
|
||||
const result = await axios.post('/courseCategory', {
|
||||
name: id.value
|
||||
})
|
||||
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
async store(form) {
|
||||
const result = await axios.post('/admin/categories', form)
|
||||
|
||||
if (result.status === 201)
|
||||
this.items.unshift(result.data)
|
||||
|
||||
return result;
|
||||
},
|
||||
async update(id, form) {
|
||||
const result = await axios.put(`/admin/categories/${id}`, form)
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items[index] = result.data
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
async remove(id) {
|
||||
const result = await axios.delete(`/courseCategory/${id}`)
|
||||
|
||||
if (result.status === 200) {
|
||||
const index = this.items.findIndex(({ _id }) => _id == id)
|
||||
this.items.splice(index, 1)
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
}
|
||||
})
|
||||
@@ -1,14 +0,0 @@
|
||||
export const formatSize = (bytes) => {
|
||||
const k = 1024;
|
||||
const dm = 1;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
if (bytes === 0) {
|
||||
return `0 ${sizes[0]}`;
|
||||
}
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
|
||||
|
||||
return `${formattedSize} ${sizes[i]}`;
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
export const treeTogglerRTL = (selector) => {
|
||||
try {
|
||||
document.querySelectorAll(selector).forEach(element => {
|
||||
element.style['margin-right'] = element.style['margin-left']
|
||||
element.style['margin-left'] = null
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
+77
-99
@@ -1,109 +1,87 @@
|
||||
<template>
|
||||
<section class="flex flex-col gap-4">
|
||||
<Panel :header="$t('اطلاعات صفحه ی درباره ما')">
|
||||
<div class="grid grid-cols-2 gap-7">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" v-model="data.expertise" />
|
||||
<label>{{ $t("تعداد حوزه های تخصصی") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" v-model="data.student" />
|
||||
<label>{{ $t("تعداد دانشجو ها") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<section class="flex flex-col gap-4">
|
||||
<Panel :header="$t('اطلاعات صفحه ی درباره ما')">
|
||||
<div class="grid grid-cols-2 gap-7">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" v-model="data.expertise" />
|
||||
<label>{{ $t("تعداد حوزه های تخصصی") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-7 mt-7">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="data.title" class="w-full" />
|
||||
<label>{{ $t("تعداد عناوین آموزشی") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" v-model="data.hours" />
|
||||
<label>{{ $t("تعداد ساعات آموزشی") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" v-model="data.student" />
|
||||
<label>{{ $t("تعداد دانشجو ها") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<div class="flex pt-4 justify-end">
|
||||
<Button
|
||||
:label="$t('edit')"
|
||||
icon="pi pi-save"
|
||||
severity="success"
|
||||
:loading="saving"
|
||||
@click="save(data)"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-7 mt-7">
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText v-model="data.title" class="w-full" />
|
||||
<label>{{ $t("تعداد عناوین آموزشی") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
</Panel>
|
||||
</section>
|
||||
</template>
|
||||
<div class="flex flex-col gap-1">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" v-model="data.hours" />
|
||||
<label>{{ $t("تعداد ساعات آموزشی") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex pt-4 justify-end">
|
||||
<Button
|
||||
:label="$t('edit')"
|
||||
icon="pi pi-save"
|
||||
severity="success"
|
||||
:loading="saving"
|
||||
@click="save(data)"
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
|
||||
import {
|
||||
inject,
|
||||
defineAsyncComponent,
|
||||
watchEffect,
|
||||
ref,
|
||||
watch,
|
||||
nextTick,
|
||||
reactive,
|
||||
} from "vue";
|
||||
import { useStaticsStore } from "@/stores/statics";
|
||||
import { useRoute } from "vue-router";
|
||||
const data = reactive({
|
||||
expertise: 0,
|
||||
student: 0,
|
||||
title: 0,
|
||||
hours: 0,
|
||||
});
|
||||
const saving = ref(false);
|
||||
const save = async (data) => {
|
||||
saving.value = true;
|
||||
const result = await store.edit(data);
|
||||
|
||||
if (result.data === true){
|
||||
|
||||
toast.add({
|
||||
life: 2000,
|
||||
severity: "success",
|
||||
summary: t("successful"),
|
||||
detail: t("اطلاعات با موفقیت ویرایش شد"),
|
||||
import { inject, watchEffect, ref, reactive } from "vue";
|
||||
import { useStaticsStore } from "@/stores/statics";
|
||||
const { toast, t } = inject("service");
|
||||
const store = useStaticsStore();
|
||||
const data = reactive({
|
||||
expertise: 0,
|
||||
student: 0,
|
||||
title: 0,
|
||||
hours: 0,
|
||||
});
|
||||
const saving = ref(false);
|
||||
const save = async (data) => {
|
||||
saving.value = true;
|
||||
const result = await store.edit(data);
|
||||
|
||||
}
|
||||
else
|
||||
return toast.add({
|
||||
life: 2000,
|
||||
severity: "error",
|
||||
summary: t("error"),
|
||||
detail: data.msg,
|
||||
if (result.data === true) {
|
||||
toast.add({
|
||||
life: 2000,
|
||||
severity: "success",
|
||||
summary: t("successful"),
|
||||
detail: t("اطلاعات با موفقیت ویرایش شد"),
|
||||
});
|
||||
} else
|
||||
return toast.add({
|
||||
life: 2000,
|
||||
severity: "error",
|
||||
summary: t("error"),
|
||||
detail: data.msg,
|
||||
});
|
||||
|
||||
saving.value = false;
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
await store.index();
|
||||
data.expertise = store.items.expertise;
|
||||
data.student = store.items.student;
|
||||
data.title = store.items.title;
|
||||
data.hours = store.items.hours;
|
||||
});
|
||||
|
||||
saving.value = false;
|
||||
|
||||
};
|
||||
const expandedKeys = ref({});
|
||||
|
||||
const CategoryForm = defineAsyncComponent(() =>
|
||||
import("../components/CategoryForm.vue")
|
||||
);
|
||||
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
|
||||
const store = useStaticsStore();
|
||||
|
||||
watchEffect(async () => {
|
||||
await store.index();
|
||||
data.expertise = store.items.expertise;
|
||||
data.student = store.items.student;
|
||||
data.title = store.items.title;
|
||||
data.hours = store.items.hours;
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
+111
-156
@@ -1,169 +1,124 @@
|
||||
<template>
|
||||
|
||||
<Panel :header="$t('لیست ویدیو های دوره')">
|
||||
<template #icons>
|
||||
<Panel :header="$t('لیست ویدیو های دوره')">
|
||||
<template #icons>
|
||||
<Button
|
||||
icon="pi pi-plus"
|
||||
:label="$t('افزودن ویدیو')"
|
||||
severity="success"
|
||||
@click="create()"
|
||||
/>
|
||||
</template>
|
||||
<DataTable :value="videos">
|
||||
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
|
||||
<Column field="description" :header="$t('description')" />
|
||||
<Column :header="$t('options')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
icon="pi pi-plus"
|
||||
:label="$t('افزودن ویدیو')"
|
||||
severity="success"
|
||||
@click="create()"
|
||||
icon="pi pi-eye"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
</template>
|
||||
<DataTable :value="videos">
|
||||
|
||||
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
|
||||
<Column field="description" :header="$t('description')" />
|
||||
<Column :header="$t('options')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button icon="pi pi-eye" rounded text severity="secondary"
|
||||
@click="show(data)" />
|
||||
<Button
|
||||
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
<Button
|
||||
@click="remove(data)"
|
||||
icon="pi pi-trash"
|
||||
rounded
|
||||
text
|
||||
severity="danger"
|
||||
:loading="data.removing"
|
||||
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<!-- <Column :field="({ price }) => numberFormat(price)" :header="$t('price')" />
|
||||
<Column :header="$t('category')" class="whitespace-nowrap">
|
||||
<template #body="{ data: { category } }">
|
||||
<span v-if="category.length > 0">
|
||||
{{ category.slice(-1)[0].name }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="name" :header="$t('reviews')">
|
||||
<template #body="{ data: { name } }">
|
||||
<Rating :modelValue="name" readonly :cancel="false" class="[&_svg]:text-yellow-400" />
|
||||
</template>
|
||||
</Column>
|
||||
<Column :header="$t('status')">
|
||||
<template #body="{ data: { confirmToShow, _id } }">
|
||||
<Tag v-if="!confirmToShow" value="غیر فعال" severity="danger" @click="accepter(_id,true)"/>
|
||||
<Tag v-if="confirmToShow" value="فعال" severity="success" @click="accepter(_id,false)"/>
|
||||
</template>
|
||||
|
||||
</Column>
|
||||
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" class="w-28" />
|
||||
<Column :field="({ updatedAt }) => jDate(updatedAt)" :header="$t('updatedAt')" class="w-32 text-center" />
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
|
||||
<Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(data)" />
|
||||
<Button icon="pi pi-trash" rounded text severity="danger" :loading="data.removing"
|
||||
@click="remove(data)" />
|
||||
</div>
|
||||
</template>
|
||||
</Column> -->
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
<Button
|
||||
@click="remove(data)"
|
||||
icon="pi pi-trash"
|
||||
rounded
|
||||
text
|
||||
severity="danger"
|
||||
:loading="data.removing"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<!-- <template #footer>
|
||||
<Pagination :total="store.total" />
|
||||
</template> -->
|
||||
</DataTable>
|
||||
<!-- <Galleria
|
||||
v-model:visible="galleryVes"
|
||||
:value="gallery"
|
||||
:numVisible="5"
|
||||
containerClass="w-1/2"
|
||||
circular
|
||||
fullScreen
|
||||
showItemNavigators
|
||||
:showThumbnailNavigators="false"
|
||||
> -->
|
||||
|
||||
</Panel>
|
||||
</template>
|
||||
</Column>
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
</DataTable>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
const { toast, dialog, confirm, t } = inject('service')
|
||||
import {
|
||||
ref,
|
||||
watchEffect,
|
||||
reactive,
|
||||
defineAsyncComponent,
|
||||
inject,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useHeadlinesStore } from "@/stores/Headlines";
|
||||
const store = useHeadlinesStore();
|
||||
const route = useRoute();
|
||||
const videos = ref();
|
||||
const { dialog, confirm, t } = inject("service");
|
||||
const HeadlineDetail = defineAsyncComponent(() =>
|
||||
import("../components/HeadlineDetail.vue")
|
||||
);
|
||||
const AddVideo = defineAsyncComponent(() =>
|
||||
import("../components/AddVideo.vue")
|
||||
);
|
||||
const EditVideo = defineAsyncComponent(() =>
|
||||
import("../components/EditVideo.vue")
|
||||
);
|
||||
|
||||
import { ref, watchEffect, reactive, defineAsyncComponent, inject, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useHeadlinesStore } from "@/stores/Headlines"
|
||||
const store = useHeadlinesStore()
|
||||
const route = useRoute()
|
||||
const videos = ref()
|
||||
const HeadlineDetail = defineAsyncComponent(() => import('../components/HeadlineDetail.vue'));
|
||||
const AddVideo = defineAsyncComponent(() => import('../components/AddVideo.vue'));
|
||||
const EditVideo = defineAsyncComponent(() => import('../components/EditVideo.vue'));
|
||||
|
||||
watchEffect(async() => {
|
||||
videos.value = await store.index(route.params.id)
|
||||
})
|
||||
watch(videos, async()=>{
|
||||
videos.value = await store.index(route.params.id)
|
||||
})
|
||||
watchEffect(async () => {
|
||||
videos.value = await store.index(route.params.id);
|
||||
});
|
||||
watch(videos, async () => {
|
||||
videos.value = await store.index(route.params.id);
|
||||
});
|
||||
const show = (product) => {
|
||||
dialog.open(HeadlineDetail, {
|
||||
props: { modal: true, closable: true, header: t('پیش نمایش ویدیو') },
|
||||
data: {
|
||||
product
|
||||
}
|
||||
});
|
||||
}
|
||||
const router = useRouter()
|
||||
const create = () =>{
|
||||
// router.push('/videos/create')
|
||||
dialog.open(AddVideo, {
|
||||
props: { modal: true, closable: true, header: t('افزودن ویدیو') },
|
||||
data: {
|
||||
produc : route.params.id
|
||||
}
|
||||
});
|
||||
}
|
||||
dialog.open(HeadlineDetail, {
|
||||
props: { modal: true, closable: true, header: t("پیش نمایش ویدیو") },
|
||||
data: {
|
||||
product,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const create = () => {
|
||||
dialog.open(AddVideo, {
|
||||
props: { modal: true, closable: true, header: t("افزودن ویدیو") },
|
||||
data: {
|
||||
produc: route.params.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const edit = (product) => {
|
||||
dialog.open(EditVideo, {
|
||||
props: { modal: true, closable: true, header: t('ویرایش ویدیو') },
|
||||
data: {
|
||||
product
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const remove = async(data) => {
|
||||
confirm.require({
|
||||
message: t('Are you sure you want to proceed?'),
|
||||
header: t('Danger Zone'),
|
||||
icon: 'pi pi-info-circle',
|
||||
acceptClass: 'p-button-danger p-button-sm',
|
||||
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
|
||||
rejectLabel: t('cancel'),
|
||||
acceptLabel: t('beOmitted'),
|
||||
defaultFocus: 'reject',
|
||||
accept: async () => {
|
||||
await store.delete(data.id)
|
||||
await store.index(route.params.id)
|
||||
// if (status === 200)
|
||||
// toast.add({
|
||||
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
// });
|
||||
// else
|
||||
// toast.add({
|
||||
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
// });
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
dialog.open(EditVideo, {
|
||||
props: { modal: true, closable: true, header: t("ویرایش ویدیو") },
|
||||
data: {
|
||||
product,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const remove = async (data) => {
|
||||
confirm.require({
|
||||
message: t("Are you sure you want to proceed?"),
|
||||
header: t("Danger Zone"),
|
||||
icon: "pi pi-info-circle",
|
||||
acceptClass: "p-button-danger p-button-sm",
|
||||
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
|
||||
rejectLabel: t("cancel"),
|
||||
acceptLabel: t("beOmitted"),
|
||||
defaultFocus: "reject",
|
||||
accept: async () => {
|
||||
await store.delete(data.id);
|
||||
await store.index(route.params.id);
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -32,22 +32,20 @@
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
|
||||
|
||||
</DataTable>
|
||||
</Panel>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useUsersStore } from "@/stores/users";
|
||||
import { inject, reactive, defineAsyncComponent, ref, watchEffect, watch } from "vue";
|
||||
import { inject, defineAsyncComponent, ref, watchEffect, watch } from "vue";
|
||||
const store = useUsersStore();
|
||||
const admins = ref()
|
||||
|
||||
const UserDetails = defineAsyncComponent(() =>
|
||||
import("../components/UserDetails.vue")
|
||||
);
|
||||
const { dialog, t } = inject("service");
|
||||
|
||||
|
||||
const show = (user) => {
|
||||
dialog.open(UserDetails, {
|
||||
props: { modal: true, closable: true, header: t("userDetails") },
|
||||
@@ -60,7 +58,6 @@ watchEffect(async () => {
|
||||
});
|
||||
watch(admins, async()=>{
|
||||
await store.index();
|
||||
|
||||
admins.value = store.items.user.filter(item => item.roles[1] === 'Admin' );
|
||||
})
|
||||
</script>
|
||||
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<Panel :header="$t('پاسخ به نظرات')">
|
||||
<div v-for="item in data.comments" :key="item.id">
|
||||
|
||||
<div class="card mb-8">
|
||||
<Panel toggleable>
|
||||
<template #header>
|
||||
@@ -31,10 +30,6 @@
|
||||
<p v-for="answr in item.replies" :key="answr.id" class="border rounded-md ms-[60px] mt-2 p-2">{{answr.text}}</p>
|
||||
</Panel>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<Textarea v-model="value" placeholder="پاسخ..." class="w-full" rows="5" />
|
||||
@@ -49,10 +44,6 @@
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
@@ -72,10 +63,8 @@ const store = useProductsStore()
|
||||
const commentStore = useCommentStore()
|
||||
const route = useRoute()
|
||||
const data = ref([])
|
||||
|
||||
const menu = ref(null);
|
||||
const toast = useToast();
|
||||
const router = useRouter();
|
||||
const replyTarget = ref(null)
|
||||
const value = ref(null)
|
||||
const reply = (id) =>{
|
||||
@@ -86,14 +75,11 @@ const sendReply = async() =>{
|
||||
|
||||
}
|
||||
if (value.value === null || value.value === '') {
|
||||
|
||||
}
|
||||
|
||||
const rep = await commentStore.reply(route.params.id, {
|
||||
text: value.value,
|
||||
parent: replyTarget.value
|
||||
})
|
||||
|
||||
}
|
||||
const items = ref([
|
||||
{
|
||||
@@ -113,14 +99,6 @@ const items = ref([
|
||||
}
|
||||
]);
|
||||
|
||||
const toggle = (event) => {
|
||||
menu.value.toggle(event);
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
toast.add({ severity: 'success', summary: 'Success', detail: 'Data Saved', life: 3000 });
|
||||
};
|
||||
|
||||
watchEffect(async() => {
|
||||
data.value = await store.edit(route.params.id)
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
<Column headerClass="w-32" :header="$t('options')">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-start">
|
||||
<!-- <Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(categories)" />
|
||||
-->
|
||||
<Button
|
||||
icon="pi pi-trash"
|
||||
rounded
|
||||
@@ -37,26 +35,19 @@
|
||||
|
||||
<script setup>
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
|
||||
import {
|
||||
inject,
|
||||
defineAsyncComponent,
|
||||
watchEffect,
|
||||
ref,
|
||||
watch,
|
||||
nextTick,
|
||||
watch
|
||||
} from "vue";
|
||||
import { useCategoriesStore } from "@/stores/categoreis";
|
||||
import { useRoute } from "vue-router";
|
||||
const categories = ref();
|
||||
// const expandedKeys = ref({});
|
||||
|
||||
const CategoryForm = defineAsyncComponent(() =>
|
||||
import("../components/CategoryForm.vue")
|
||||
);
|
||||
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
|
||||
const store = useCategoriesStore();
|
||||
|
||||
const create = async() => {
|
||||
@@ -66,14 +57,6 @@ const create = async() => {
|
||||
});
|
||||
await store.index(1,100)
|
||||
};
|
||||
|
||||
// const edit = (category) => {
|
||||
// dialog.open(CategoryForm, {
|
||||
// props: { modal: true, closable: false, header: t('editCategory') },
|
||||
// data: { categories: store.items, category }
|
||||
// });
|
||||
// };
|
||||
|
||||
const remove = (data) => {
|
||||
confirm.require({
|
||||
message: t("Are you sure you want to proceed?"),
|
||||
@@ -115,4 +98,3 @@ watch(categories ,async () => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
+101
-142
@@ -1,66 +1,36 @@
|
||||
<template>
|
||||
<Panel :header="$t('comments')">
|
||||
<DataTable :value="store.items">
|
||||
<Column class="w-16" :header="$t('image')">
|
||||
<template #body="{ data: { image } }">
|
||||
<Avatar
|
||||
:image="image"
|
||||
size="large"
|
||||
shape="circle"
|
||||
class="transition-transform hover:scale-150"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="name" :header="$t('name')" />
|
||||
<Column
|
||||
:field="({ categories }) => categories[0].name"
|
||||
:header="$t('category')"
|
||||
/>
|
||||
|
||||
<Column field="price" :header="$t('price')" />
|
||||
<Column
|
||||
:field="({ createdAt }) => jDate(createdAt)"
|
||||
:header="$t('createdAt')"
|
||||
class="w-28"
|
||||
/>
|
||||
<Column
|
||||
:field="({ updatedAt }) => jDate(updatedAt)"
|
||||
:header="$t('updatedAt')"
|
||||
class="w-32 text-center"
|
||||
/>
|
||||
<Column field="averageRating" :header="$t('امتیاز')" />
|
||||
|
||||
<Column :header="$t('مشاهده نظرات')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
|
||||
<Panel :header="$t('comments')">
|
||||
<DataTable :value="store.items.courses">
|
||||
<Column class="w-16" :header="$t('image')">
|
||||
<template #body="{ data: { image } }">
|
||||
<Avatar
|
||||
:image="image"
|
||||
size="large"
|
||||
shape="circle"
|
||||
class="transition-transform hover:scale-150"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="name" :header="$t('name')" />
|
||||
<Column
|
||||
:field="({ categories }) => categories[0].name"
|
||||
:header="$t('category')"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
<!-- <template #footer>
|
||||
<Pagination :total="store.total" />
|
||||
</template> -->
|
||||
</DataTable>
|
||||
<Galleria
|
||||
v-model:visible="galleryVes"
|
||||
:value="gallery"
|
||||
:numVisible="5"
|
||||
containerClass="w-1/2"
|
||||
circular
|
||||
fullScreen
|
||||
showItemNavigators
|
||||
:showThumbnailNavigators="false"
|
||||
>
|
||||
<template #item="{ item: { itemImageSrc, alt } }">
|
||||
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
|
||||
</template>
|
||||
</Galleria>
|
||||
<Column headerClass="w-32">
|
||||
<Column :field="({ price }) => numberFormat(price)" :header="$t('price')" />
|
||||
<Column
|
||||
:field="({ createdAt }) => jDate(createdAt)"
|
||||
:header="$t('createdAt')"
|
||||
class="w-28"
|
||||
/>
|
||||
<Column
|
||||
:field="({ updatedAt }) => jDate(updatedAt)"
|
||||
:header="$t('updatedAt')"
|
||||
class="w-32 text-center"
|
||||
/>
|
||||
<Column field="averageRating" :header="$t('امتیاز')" />
|
||||
|
||||
<Column :header="$t('مشاهده نظرات')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
@@ -73,89 +43,78 @@
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
<Pagination :total="store.items.count" />
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
icon="pi pi-eye"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useProductsStore } from '@/stores/products';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const ProductForm = defineAsyncComponent(() => import('../components/ProductForm.vue'));
|
||||
const ProductDetails = defineAsyncComponent(() => import('../components/ProductDetails.vue'));
|
||||
|
||||
const { toast, dialog, confirm, t } = inject('service')
|
||||
|
||||
var gallery = []
|
||||
const galleryVes = ref(false)
|
||||
const openGallery = (images =>{
|
||||
gallery = []
|
||||
images.map(image =>{
|
||||
gallery.push({itemImageSrc:image, alt:"product"})
|
||||
})
|
||||
galleryVes.value = true
|
||||
|
||||
})
|
||||
const router = useRouter()
|
||||
const show = (data) => {
|
||||
router.push(`/comments/${data._id}`)
|
||||
}
|
||||
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
||||
|
||||
const store = useProductsStore()
|
||||
const route = useRoute()
|
||||
const edit = (data) =>{
|
||||
router.push(data._id)
|
||||
}
|
||||
|
||||
|
||||
const remove = (data) => {
|
||||
|
||||
confirm.require({
|
||||
message: t('Are you sure you want to proceed?'),
|
||||
header: t('Danger Zone'),
|
||||
icon: 'pi pi-info-circle',
|
||||
acceptClass: 'p-button-danger p-button-sm',
|
||||
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
|
||||
rejectLabel: t('cancel'),
|
||||
acceptLabel: t('beOmitted'),
|
||||
defaultFocus: 'reject',
|
||||
accept: async () => {
|
||||
const deleted = await store.delete(data._id)
|
||||
|
||||
await store.index()
|
||||
}
|
||||
import { useProductsStore } from "@/stores/products";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { numberFormat } from "@/utils/numberFormat";
|
||||
import {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
watchEffect(async() => {
|
||||
const { page = 1, pageSize = 10 } = route.query
|
||||
await store.index(page, pageSize)
|
||||
inject,
|
||||
watchEffect,
|
||||
} from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
|
||||
const { confirm, t } = inject("service");
|
||||
|
||||
|
||||
const router = useRouter();
|
||||
const show = (data) => {
|
||||
router.push(`/comments/${data._id}`);
|
||||
};
|
||||
|
||||
const store = useProductsStore();
|
||||
const route = useRoute();
|
||||
const edit = (data) => {
|
||||
router.push(data._id);
|
||||
};
|
||||
|
||||
const remove = (data) => {
|
||||
confirm.require({
|
||||
message: t("Are you sure you want to proceed?"),
|
||||
header: t("Danger Zone"),
|
||||
icon: "pi pi-info-circle",
|
||||
acceptClass: "p-button-danger p-button-sm",
|
||||
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
|
||||
rejectLabel: t("cancel"),
|
||||
acceptLabel: t("beOmitted"),
|
||||
defaultFocus: "reject",
|
||||
accept: async () => {
|
||||
const deleted = await store.delete(data._id);
|
||||
await store.index();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
const { page = 1, pageSize = 10 } = route.query;
|
||||
await store.index(page, pageSize);
|
||||
console.log(store);
|
||||
});
|
||||
</script>
|
||||
|
||||
})
|
||||
|
||||
const accepter = async (id,confirmToShow)=>{
|
||||
const { status, data } = await store.confirm(id, {confirmToShow});
|
||||
|
||||
|
||||
if (status === 200)
|
||||
toast.add({
|
||||
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
});
|
||||
else
|
||||
toast.add({
|
||||
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
+74
-64
@@ -1,74 +1,84 @@
|
||||
<template>
|
||||
<Panel :header="$t('coupons')">
|
||||
<DataTable :value="store.items.discount">
|
||||
|
||||
<Column field="code" :header="$t('کد تخفیف')" />
|
||||
<Column field="id" :header="$t('id')" />
|
||||
<Column field="admin.name" :header="$t('ایجاد شده توسط')" />
|
||||
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
|
||||
<Column :field="({ startDate }) => jDate(startDate)" :header="$t('تاریخ شروع')" />
|
||||
<Column :field="({ endDate }) => jDate(endDate)" :header="$t('تاریخ انقضا')" />
|
||||
<Column field="statusText" :header="$t('status')" />
|
||||
|
||||
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-eye"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
<!--
|
||||
<Panel :header="$t('coupons')">
|
||||
<DataTable :value="store.items.discount">
|
||||
<Column field="code" :header="$t('کد تخفیف')" />
|
||||
<Column field="id" :header="$t('id')" />
|
||||
<Column field="admin.name" :header="$t('ایجاد شده توسط')" />
|
||||
<Column
|
||||
:field="({ createdAt }) => jDate(createdAt)"
|
||||
:header="$t('createdAt')"
|
||||
/>
|
||||
<Column
|
||||
:field="({ startDate }) => jDate(startDate)"
|
||||
:header="$t('تاریخ شروع')"
|
||||
/>
|
||||
<Column
|
||||
:field="({ endDate }) => jDate(endDate)"
|
||||
:header="$t('تاریخ انقضا')"
|
||||
/>
|
||||
<Column field="statusText" :header="$t('status')" />
|
||||
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-eye"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
|
||||
<Pagination :total="store.total" />
|
||||
</template> -->
|
||||
</DataTable>
|
||||
</Panel>
|
||||
<Pagination :total="store.items.count" />
|
||||
</template>
|
||||
</DataTable>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import {useDiscountStore} from "../stores/discounts"
|
||||
import { useDiscountStore } from "../stores/discounts";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import {
|
||||
defineAsyncComponent,
|
||||
inject,
|
||||
watchEffect,
|
||||
} from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
const route = useRoute();
|
||||
const { dialog, t } = inject("service");
|
||||
const CouponDetails = defineAsyncComponent(() =>
|
||||
import("../components/CouponDetails.vue")
|
||||
);
|
||||
|
||||
const { toast, dialog, confirm, t } = inject('service')
|
||||
const CouponDetails = defineAsyncComponent(() => import('../components/CouponDetails.vue'));
|
||||
|
||||
const store = useDiscountStore()
|
||||
const store = useDiscountStore();
|
||||
|
||||
const show = (coupon) => {
|
||||
dialog.open(CouponDetails, {
|
||||
props: { modal: true, closable: true, header: t('جزئیات تخفیف') },
|
||||
data: {
|
||||
coupon
|
||||
}
|
||||
});
|
||||
}
|
||||
const router = useRouter()
|
||||
const edit = (data) =>{
|
||||
|
||||
router.push(`/coupon/${data.id}`)
|
||||
}
|
||||
watchEffect( async() =>{
|
||||
await store.index()
|
||||
})
|
||||
dialog.open(CouponDetails, {
|
||||
props: { modal: true, closable: true, header: t("جزئیات تخفیف") },
|
||||
data: {
|
||||
coupon,
|
||||
},
|
||||
});
|
||||
};
|
||||
const router = useRouter();
|
||||
const edit = (data) => {
|
||||
router.push(`/coupon/${data.id}`);
|
||||
};
|
||||
watchEffect(async () => {
|
||||
const { page = 1, pageSize = 10 } = route.query;
|
||||
await store.index(page, pageSize);
|
||||
});
|
||||
</script>
|
||||
+67
-144
@@ -8,7 +8,7 @@
|
||||
@click="create()"
|
||||
/>
|
||||
</template>
|
||||
<DataTable :value="store.items">
|
||||
<DataTable :value="store.items.courses">
|
||||
<Column class="w-16" :header="$t('image')">
|
||||
<template #body="{ data: { image } }">
|
||||
<Avatar
|
||||
@@ -25,7 +25,10 @@
|
||||
:header="$t('category')"
|
||||
/>
|
||||
|
||||
<Column field="price" :header="$t('price')" />
|
||||
<Column
|
||||
:field="({ price }) => numberFormat(price)"
|
||||
:header="$t('price')"
|
||||
/>
|
||||
<Column
|
||||
:field="({ createdAt }) => jDate(createdAt)"
|
||||
:header="$t('createdAt')"
|
||||
@@ -41,7 +44,13 @@
|
||||
<Column :header="$t('options')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
|
||||
<Button
|
||||
icon="pi pi-eye"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
rounded
|
||||
@@ -49,72 +58,27 @@
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
|
||||
|
||||
<Button
|
||||
@click="remove(data)"
|
||||
@click="remove(data)"
|
||||
icon="pi pi-trash"
|
||||
rounded
|
||||
text
|
||||
severity="danger"
|
||||
:loading="data.removing"
|
||||
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
<!-- <Column :field="({ price }) => numberFormat(price)" :header="$t('price')" />
|
||||
<Column :header="$t('category')" class="whitespace-nowrap">
|
||||
<template #body="{ data: { category } }">
|
||||
<span v-if="category.length > 0">
|
||||
{{ category.slice(-1)[0].name }}
|
||||
</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="name" :header="$t('reviews')">
|
||||
<template #body="{ data: { name } }">
|
||||
<Rating :modelValue="name" readonly :cancel="false" class="[&_svg]:text-yellow-400" />
|
||||
</template>
|
||||
</Column>
|
||||
<Column :header="$t('status')">
|
||||
<template #body="{ data: { confirmToShow, _id } }">
|
||||
<Tag v-if="!confirmToShow" value="غیر فعال" severity="danger" @click="accepter(_id,true)"/>
|
||||
<Tag v-if="confirmToShow" value="فعال" severity="success" @click="accepter(_id,false)"/>
|
||||
</template>
|
||||
|
||||
</Column>
|
||||
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" class="w-28" />
|
||||
<Column :field="({ updatedAt }) => jDate(updatedAt)" :header="$t('updatedAt')" class="w-32 text-center" />
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
|
||||
<Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(data)" />
|
||||
<Button icon="pi pi-trash" rounded text severity="danger" :loading="data.removing"
|
||||
@click="remove(data)" />
|
||||
</div>
|
||||
</template>
|
||||
</Column> -->
|
||||
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
<!-- <template #footer>
|
||||
<template #footer>
|
||||
<Pagination :total="store.items.count" />
|
||||
</template> -->
|
||||
</DataTable>
|
||||
<Galleria
|
||||
v-model:visible="galleryVes"
|
||||
:value="gallery"
|
||||
:numVisible="5"
|
||||
containerClass="w-1/2"
|
||||
circular
|
||||
fullScreen
|
||||
showItemNavigators
|
||||
:showThumbnailNavigators="false"
|
||||
>
|
||||
<template #item="{ item: { itemImageSrc, alt } }">
|
||||
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
|
||||
</template>
|
||||
</Galleria>
|
||||
</DataTable>
|
||||
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
@@ -132,104 +96,63 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useProductsStore } from '@/stores/products';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useProductsStore } from "@/stores/products";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { numberFormat } from "@/utils/numberFormat";
|
||||
import {
|
||||
defineAsyncComponent,
|
||||
inject,
|
||||
onBeforeMount,
|
||||
ref,
|
||||
watchEffect,
|
||||
} from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
const ProductForm = defineAsyncComponent(() => import('../components/ProductForm.vue'));
|
||||
const ProductDetails = defineAsyncComponent(() => import('../components/ProductDetails.vue'));
|
||||
const ProductDetails = defineAsyncComponent(() =>
|
||||
import("../components/ProductDetails.vue")
|
||||
);
|
||||
|
||||
const { toast, dialog, confirm, t } = inject('service')
|
||||
const { toast, dialog, confirm, t } = inject("service");
|
||||
|
||||
var gallery = []
|
||||
const galleryVes = ref(false)
|
||||
const openGallery = (images =>{
|
||||
gallery = []
|
||||
images.map(image =>{
|
||||
gallery.push({itemImageSrc:image, alt:"product"})
|
||||
})
|
||||
galleryVes.value = true
|
||||
|
||||
})
|
||||
const show = (product) => {
|
||||
dialog.open(ProductDetails, {
|
||||
props: { modal: true, closable: true, header: t('productDetails') },
|
||||
data: {
|
||||
product
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
||||
|
||||
const store = useProductsStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const edit = (data) =>{
|
||||
router.push(data._id)
|
||||
}
|
||||
dialog.open(ProductDetails, {
|
||||
props: { modal: true, closable: true, header: t("productDetails") },
|
||||
data: {
|
||||
product,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const store = useProductsStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const edit = (data) => {
|
||||
router.push(data._id);
|
||||
};
|
||||
|
||||
const remove = (data) => {
|
||||
confirm.require({
|
||||
message: t("Are you sure you want to proceed?"),
|
||||
header: t("Danger Zone"),
|
||||
icon: "pi pi-info-circle",
|
||||
acceptClass: "p-button-danger p-button-sm",
|
||||
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
|
||||
rejectLabel: t("cancel"),
|
||||
acceptLabel: t("beOmitted"),
|
||||
defaultFocus: "reject",
|
||||
accept: async () => {
|
||||
const deleted = await store.delete(data._id);
|
||||
|
||||
confirm.require({
|
||||
message: t('Are you sure you want to proceed?'),
|
||||
header: t('Danger Zone'),
|
||||
icon: 'pi pi-info-circle',
|
||||
acceptClass: 'p-button-danger p-button-sm',
|
||||
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
|
||||
rejectLabel: t('cancel'),
|
||||
acceptLabel: t('beOmitted'),
|
||||
defaultFocus: 'reject',
|
||||
accept: async () => {
|
||||
const deleted = await store.delete(data._id)
|
||||
|
||||
await store.index()
|
||||
}
|
||||
// product.removing = true
|
||||
await store.index();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// const { status, data } = await store.remove(product._id);
|
||||
|
||||
// product.removing = false
|
||||
|
||||
// if (status === 200)
|
||||
// toast.add({
|
||||
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
// });
|
||||
// else
|
||||
// toast.add({
|
||||
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
// });
|
||||
|
||||
// },
|
||||
});
|
||||
}
|
||||
|
||||
const create = () => router.push('/create')
|
||||
|
||||
|
||||
|
||||
watchEffect(async() => {
|
||||
const { page = 1, pageSize = 10 } = route.query
|
||||
await store.index(page, pageSize)
|
||||
|
||||
})
|
||||
|
||||
const accepter = async (id,confirmToShow)=>{
|
||||
const { status, data } = await store.confirm(id, {confirmToShow});
|
||||
|
||||
|
||||
if (status === 200)
|
||||
toast.add({
|
||||
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
});
|
||||
else
|
||||
toast.add({
|
||||
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
});
|
||||
}
|
||||
const create = () => router.push("/create");
|
||||
watchEffect(async () => {
|
||||
const { page = 1, pageSize = 10 } = route.query;
|
||||
await store.index(page, pageSize);
|
||||
console.log(store.items);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
+4
-121
@@ -23,7 +23,6 @@
|
||||
:options="['انگلیسی', 'فارسی']"
|
||||
class="w-full"
|
||||
/>
|
||||
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
|
||||
<label>{{ $t("زبان آموزش") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
@@ -139,22 +138,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="flex flex-col gap-1">
|
||||
<InputGroup>
|
||||
<FloatLabel>
|
||||
<Dropdown
|
||||
:options="cities"
|
||||
:optionLabel="cities.name"
|
||||
:optionValue="cities.name"
|
||||
class="w-full"
|
||||
>
|
||||
</Dropdown>
|
||||
<label>{{ $t("brand") }}</label>
|
||||
</FloatLabel>
|
||||
</InputGroup>
|
||||
|
||||
</div> -->
|
||||
|
||||
|
||||
<div class="flex pt-4 justify-end">
|
||||
<Button
|
||||
@@ -179,16 +163,12 @@
|
||||
|
||||
<script setup>
|
||||
import Listbox from "primevue/listbox";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
|
||||
|
||||
import {
|
||||
inject,
|
||||
defineAsyncComponent,
|
||||
watchEffect,
|
||||
ref,
|
||||
watch,
|
||||
nextTick,
|
||||
reactive,
|
||||
} from "vue";
|
||||
|
||||
@@ -231,10 +211,10 @@ const selectLevel = () => {
|
||||
const selectLanguage = () => {
|
||||
newCourse.language = language.value;
|
||||
};
|
||||
const selectedCity = ref();
|
||||
|
||||
const cities = ref();
|
||||
const teachers = ref();
|
||||
const levels = reactive(["آسان", "متوسط", "سخت"]);
|
||||
|
||||
const newCourse = reactive({
|
||||
name: "",
|
||||
description: "",
|
||||
@@ -332,13 +312,8 @@ const create = async (newCourse) => {
|
||||
|
||||
};
|
||||
|
||||
const expandedKeys = ref({});
|
||||
|
||||
const CategoryForm = defineAsyncComponent(() =>
|
||||
import("../components/CategoryForm.vue")
|
||||
);
|
||||
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
const { toast, t } = inject("service");
|
||||
|
||||
const store = useStaticsStore();
|
||||
|
||||
@@ -347,95 +322,3 @@ watchEffect(async () => {
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<script setup>
|
||||
import { useUploaderStore } from "@/stores/uploader";
|
||||
import FirstStep from "../components/ProductFormSteps/FirstStep.vue";
|
||||
import SecondStep from "../components/ProductFormSteps/SecondStep.vue";
|
||||
import ThirdStep from "../components/ProductFormSteps/ThirdStep.vue";
|
||||
|
||||
import { inject, provide, reactive, ref } from "vue";
|
||||
import { useProductsStore } from "@/stores/products";
|
||||
|
||||
const dialog = inject("dialogRef");
|
||||
const { toast, t } = inject("service");
|
||||
// const { product } = dialog.data
|
||||
|
||||
//const form = reactive(Object.assign({}, product || {}));
|
||||
//console.log(form);
|
||||
const errors = reactive({});
|
||||
|
||||
const saving = ref(false);
|
||||
|
||||
provide("data", { errors, saving });
|
||||
|
||||
const store = useProductsStore();
|
||||
|
||||
const uploader = useUploaderStore();
|
||||
|
||||
const save = async () => {
|
||||
saving.value = true;
|
||||
|
||||
|
||||
|
||||
//Upload File
|
||||
if (typeof form.coverImage === "object") {
|
||||
const { status, data } = await uploader.single(form.coverImage);
|
||||
|
||||
if (status === 201) form.coverImage = data.url;
|
||||
else
|
||||
return toast.add({
|
||||
life: 2000,
|
||||
severity: "error",
|
||||
summary: t("error"),
|
||||
detail: data.msg,
|
||||
});
|
||||
}
|
||||
|
||||
//upload images
|
||||
const files = form.images.filter((image) => typeof image === "object");
|
||||
if (files.length > 0) {
|
||||
const { status, data } = await uploader.bulk(files);
|
||||
|
||||
if (status === 201)
|
||||
form.images = form.images.map((image) => {
|
||||
if (typeof image === "object") return data.urls.shift();
|
||||
return image;
|
||||
});
|
||||
else
|
||||
return toast.add({
|
||||
life: 2000,
|
||||
severity: "error",
|
||||
summary: t("error"),
|
||||
detail: data.msg,
|
||||
});
|
||||
}
|
||||
|
||||
//Create or Update
|
||||
// const { status, data } = !!product
|
||||
// ? await store.update(product._id, form)
|
||||
// : await store.store(form);
|
||||
|
||||
// saving.value = false;
|
||||
|
||||
// if (status === 200 || status === 201) {
|
||||
// toast.add({
|
||||
// life: 2000,
|
||||
// severity: "success",
|
||||
// summary: t("successful"),
|
||||
// detail: t(!!category ? "updateSuccessfully" : "storeSuccessfully"),
|
||||
// });
|
||||
// dialog.value.close();
|
||||
// } else if (status === 422) Object.assign(errors, data);
|
||||
// else
|
||||
// toast.add({
|
||||
// severity: "error",
|
||||
// life: 2000,
|
||||
// detail: data.msg,
|
||||
// summary: t("error"),
|
||||
// });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style> -->
|
||||
@@ -69,12 +69,7 @@
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex flex-col gap-1 my-custom-container inline">
|
||||
<FloatLabel>
|
||||
<InputText class="w-full" />
|
||||
<label>{{ $t("تخفیف برای همه کاربران") }}</label>
|
||||
</FloatLabel>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex pt-4 justify-end">
|
||||
@@ -93,16 +88,11 @@
|
||||
<script setup>
|
||||
import { useProductsStore } from "@/stores/products";
|
||||
import { useDiscountStore } from "@/stores/discounts";
|
||||
import { ref, watchEffect, reactive, inject } from "vue";
|
||||
import moment from "moment-jalaali";
|
||||
import { ref, reactive, inject } from "vue";
|
||||
|
||||
import { useRouter } from "vue-router";
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
const date = ref(null);
|
||||
const date2 = ref(null);
|
||||
const noe = ref("مبلغ");
|
||||
const store = useProductsStore();
|
||||
const { toast, t } = inject("service");
|
||||
const discount = useDiscountStore();
|
||||
const data = ref(null);
|
||||
const type = ref(null);
|
||||
const router = useRouter()
|
||||
const setType = (val) => {
|
||||
@@ -113,8 +103,6 @@ const setType = (val) => {
|
||||
}
|
||||
};
|
||||
|
||||
//const { dialog, confirm, toast, t } = inject("service");
|
||||
|
||||
const coupon = reactive({
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
|
||||
+2
-24
@@ -23,7 +23,6 @@
|
||||
:options="['انگلیسی', 'فارسی']"
|
||||
class="w-full"
|
||||
/>
|
||||
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
|
||||
<label>{{ $t("زبان آموزش") }}</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
@@ -140,22 +139,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="flex flex-col gap-1">
|
||||
<InputGroup>
|
||||
<FloatLabel>
|
||||
<Dropdown
|
||||
:options="cities"
|
||||
:optionLabel="cities.name"
|
||||
:optionValue="cities.name"
|
||||
class="w-full"
|
||||
>
|
||||
</Dropdown>
|
||||
<label>{{ $t("brand") }}</label>
|
||||
</FloatLabel>
|
||||
</InputGroup>
|
||||
|
||||
</div> -->
|
||||
|
||||
|
||||
<div class="flex pt-4 justify-end">
|
||||
<Button
|
||||
@@ -180,10 +164,7 @@
|
||||
<script setup>
|
||||
import { useProductsStore } from "@/stores/products";
|
||||
import { ref, watchEffect, reactive, inject } from "vue";
|
||||
|
||||
import { useStaticsStore } from "@/stores/statics";
|
||||
import { useUploaderStore } from "@/stores/uploader";
|
||||
|
||||
import { useCategoriesStore } from "@/stores/categoreis";
|
||||
import { useUsersStore } from "../stores/users";
|
||||
const categoriesStore = useCategoriesStore();
|
||||
@@ -192,10 +173,8 @@ const uploadStore = useUploaderStore();
|
||||
const userStore = useUsersStore();
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const route = useRoute()
|
||||
const level = ref(null);
|
||||
const selectedCategory = ref();
|
||||
const selectedteacher = ref();
|
||||
const language = ref("");
|
||||
const saving = ref(false);
|
||||
const teachers = ref();
|
||||
const creating = ref(false);
|
||||
@@ -255,7 +234,6 @@ watchEffect(async () => {
|
||||
course.totalFileSize = data.value.course.totalFileSize;
|
||||
});
|
||||
|
||||
// const levels = reactive(["آسان", "متوسط", "سخت"]);
|
||||
|
||||
const chooseImage = async (val) => {
|
||||
saving.value = true;
|
||||
@@ -278,7 +256,7 @@ const chooseImage = async (val) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
const { toast, t } = inject("service");
|
||||
const router = useRouter()
|
||||
const editCourse = async (data) => {
|
||||
creating.value = true;
|
||||
|
||||
@@ -86,7 +86,6 @@
|
||||
<script setup>
|
||||
import { useDiscountStore } from "@/stores/discounts";
|
||||
import { ref, watchEffect, reactive, inject } from "vue";
|
||||
import moment from "moment-jalaali";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
const date = ref(null);
|
||||
const date2 = ref(null);
|
||||
@@ -112,7 +111,7 @@ const discount = reactive({
|
||||
allUsers: true,
|
||||
});
|
||||
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
const { toast, t } = inject("service");
|
||||
|
||||
const route = useRoute();
|
||||
const saving = ref(false);
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@
|
||||
import { defineAsyncComponent, inject, ref, watchEffect } from "vue";
|
||||
import { useFaqStore } from "@/stores/faq";
|
||||
const store = useFaqStore();
|
||||
const { toast, dialog, confirm, t } = inject('service');
|
||||
const { dialog, t } = inject('service');
|
||||
const FaqDetails = defineAsyncComponent(() => import('../components/FaqDetails.vue'));
|
||||
const AddFaq = defineAsyncComponent(() => import('../components/AddFaq.vue'));
|
||||
const removing = ref(false)
|
||||
|
||||
@@ -66,19 +66,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
|
||||
import {
|
||||
inject,
|
||||
defineAsyncComponent,
|
||||
watchEffect,
|
||||
ref,
|
||||
watch,
|
||||
nextTick,
|
||||
reactive,
|
||||
} from "vue";
|
||||
import { useStaticsStore } from "@/stores/statics";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const data = reactive({
|
||||
header: "",
|
||||
@@ -113,13 +107,8 @@ async function save(data) {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
// const expandedKeys = ref({});
|
||||
|
||||
// const CategoryForm = defineAsyncComponent(() =>
|
||||
// import("../components/CategoryForm.vue")
|
||||
// );
|
||||
|
||||
const { dialog, confirm, toast, t } = inject("service");
|
||||
const { toast, t } = inject("service");
|
||||
|
||||
const store = useStaticsStore();
|
||||
|
||||
|
||||
+1
-13
@@ -56,7 +56,7 @@
|
||||
</Card>
|
||||
</template>
|
||||
<script setup>
|
||||
import { inject, reactive, ref } from "vue";
|
||||
import { inject, ref } from "vue";
|
||||
import { useAdminsStore } from "@/stores/admins";
|
||||
import { useRouter } from "vue-router";
|
||||
const { toast, t } = inject("service");
|
||||
@@ -109,17 +109,5 @@ const checkOtp = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// // setTimeout(() => router.push('/'), 300);
|
||||
// console.log(data);
|
||||
// } else if (status === 400)
|
||||
// // console.log('400', data.message[0])
|
||||
// errors.value = data.message[0]
|
||||
// else
|
||||
// {
|
||||
// console.log(data);
|
||||
|
||||
// }
|
||||
// toast.add({
|
||||
// severity: 'error', life: 2000, detail: data.msg, summary: t('error')
|
||||
// })
|
||||
</script>
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
اخبار
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<h1>محصولات</h1>
|
||||
</template>
|
||||
@@ -50,7 +50,6 @@ const messages = ref([]);
|
||||
const route = useRoute();
|
||||
|
||||
const send = async (value) => {
|
||||
|
||||
const sended = await store.reply({
|
||||
content: value,
|
||||
ticketId: route.params.id,
|
||||
|
||||
+3
-14
@@ -24,14 +24,7 @@
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<!--
|
||||
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
|
||||
-->
|
||||
|
||||
<Pagination :total="store.total" />
|
||||
</template>
|
||||
|
||||
</DataTable>
|
||||
</Panel>
|
||||
</template>
|
||||
@@ -39,25 +32,21 @@
|
||||
<script setup>
|
||||
import { ref, watchEffect } from "vue";
|
||||
import {useTicketsStore} from "@/stores/tickets"
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useRouter } from "vue-router";
|
||||
import {jDate} from "@/utils/jDate"
|
||||
const store = useTicketsStore()
|
||||
const router = useRouter()
|
||||
const data = ref([])
|
||||
const route = useRoute()
|
||||
const chat = (data) =>{
|
||||
router.push(`/tickets/${data.id}`)
|
||||
|
||||
}
|
||||
const closeTicket = async(data) =>{
|
||||
// console.log(data.id);
|
||||
const closess = await store.close(data.id)
|
||||
|
||||
await store.index()
|
||||
}
|
||||
watchEffect(async() =>{
|
||||
const { page = 1, pageSize = 10 } = route.query;
|
||||
data.value = await store.index(page, pageSize)
|
||||
data.value = await store.index()
|
||||
|
||||
})
|
||||
</script>
|
||||
+1
-11
@@ -34,10 +34,6 @@
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<!--
|
||||
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
|
||||
-->
|
||||
|
||||
<Pagination :total="store.items.count" />
|
||||
</template>
|
||||
</DataTable>
|
||||
@@ -48,8 +44,6 @@
|
||||
<script setup>
|
||||
import Pagination from "@/components/Pagination.vue";
|
||||
import { useUsersStore } from "@/stores/users";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
|
||||
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
@@ -61,12 +55,8 @@ const { dialog, t } = inject("service");
|
||||
|
||||
const store = useUsersStore();
|
||||
|
||||
const options = reactive([
|
||||
{ label: t("customers"), value: false },
|
||||
{ label: t("sellers"), value: true },
|
||||
]);
|
||||
|
||||
const confirmToSales = ref(false);
|
||||
|
||||
|
||||
const show = (user) => {
|
||||
dialog.open(UserDetails, {
|
||||
|
||||
+165
-164
@@ -1,76 +1,39 @@
|
||||
<template>
|
||||
<Panel :header="$t('مدیریت ویدیو ها')">
|
||||
|
||||
<DataTable :value="store.items">
|
||||
<Column class="w-16" :header="$t('image')">
|
||||
<template #body="{ data: { image } }">
|
||||
<Avatar
|
||||
:image="image"
|
||||
size="large"
|
||||
shape="circle"
|
||||
class="transition-transform hover:scale-150"
|
||||
/>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="name" :header="$t('name')" />
|
||||
<Column
|
||||
:field="({ categories }) => categories[0].name"
|
||||
:header="$t('category')"
|
||||
/>
|
||||
|
||||
<Column field="price" :header="$t('price')" />
|
||||
<Column
|
||||
:field="({ createdAt }) => jDate(createdAt)"
|
||||
:header="$t('createdAt')"
|
||||
class="w-28"
|
||||
/>
|
||||
<Column
|
||||
:field="({ updatedAt }) => jDate(updatedAt)"
|
||||
:header="$t('updatedAt')"
|
||||
class="w-32 text-center"
|
||||
/>
|
||||
<Column field="averageRating" :header="$t('امتیاز')" />
|
||||
|
||||
<Column :header="$t('options')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button icon="pi pi-eye" rounded text severity="secondary"
|
||||
@click="show(data)" />
|
||||
<Button
|
||||
icon="pi pi-video"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
<Panel :header="$t('مدیریت ویدیو ها')">
|
||||
<DataTable :value="store.items.courses">
|
||||
<Column class="w-16" :header="$t('image')">
|
||||
<template #body="{ data: { image } }">
|
||||
<Avatar
|
||||
:image="image"
|
||||
size="large"
|
||||
shape="circle"
|
||||
class="transition-transform hover:scale-150"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<Pagination :total="store.total" />
|
||||
</template>
|
||||
</DataTable>
|
||||
<Galleria
|
||||
v-model:visible="galleryVes"
|
||||
:value="gallery"
|
||||
:numVisible="5"
|
||||
containerClass="w-1/2"
|
||||
circular
|
||||
fullScreen
|
||||
showItemNavigators
|
||||
:showThumbnailNavigators="false"
|
||||
>
|
||||
<template #item="{ item: { itemImageSrc, alt } }">
|
||||
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
|
||||
</template>
|
||||
</Galleria>
|
||||
<Column headerClass="w-32">
|
||||
</Column>
|
||||
<Column field="name" :header="$t('name')" />
|
||||
<Column
|
||||
:field="({ categories }) => categories[0].name"
|
||||
:header="$t('category')"
|
||||
/>
|
||||
|
||||
<Column
|
||||
:field="({ price }) => numberFormat(price)"
|
||||
:header="$t('price')"
|
||||
/>
|
||||
<Column
|
||||
:field="({ createdAt }) => jDate(createdAt)"
|
||||
:header="$t('createdAt')"
|
||||
class="w-28"
|
||||
/>
|
||||
<Column
|
||||
:field="({ updatedAt }) => jDate(updatedAt)"
|
||||
:header="$t('updatedAt')"
|
||||
class="w-32 text-center"
|
||||
/>
|
||||
<Column field="averageRating" :header="$t('امتیاز')" />
|
||||
|
||||
<Column :header="$t('options')" headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
@@ -80,111 +43,149 @@
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-video"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="edit(data)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
<Pagination :total="store.total" />
|
||||
</template>
|
||||
</DataTable>
|
||||
<Galleria
|
||||
v-model:visible="galleryVes"
|
||||
:value="gallery"
|
||||
:numVisible="5"
|
||||
containerClass="w-1/2"
|
||||
circular
|
||||
fullScreen
|
||||
showItemNavigators
|
||||
:showThumbnailNavigators="false"
|
||||
>
|
||||
<template #item="{ item: { itemImageSrc, alt } }">
|
||||
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
|
||||
</template>
|
||||
</Galleria>
|
||||
<Column headerClass="w-32">
|
||||
<template #body="{ data }">
|
||||
<div class="flex justify-end">
|
||||
<Button
|
||||
icon="pi pi-eye"
|
||||
rounded
|
||||
text
|
||||
severity="secondary"
|
||||
@click="show(data)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Column>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useProductsStore } from '@/stores/products';
|
||||
import { jDate } from '@/utils/jDate';
|
||||
import { numberFormat } from '@/utils/numberFormat';
|
||||
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const ProductForm = defineAsyncComponent(() => import('../components/ProductForm.vue'));
|
||||
const ProductDetails = defineAsyncComponent(() => import('../components/ProductDetails.vue'));
|
||||
|
||||
const { toast, dialog, confirm, t } = inject('service')
|
||||
|
||||
var gallery = []
|
||||
const galleryVes = ref(false)
|
||||
const openGallery = (images =>{
|
||||
gallery = []
|
||||
images.map(image =>{
|
||||
gallery.push({itemImageSrc:image, alt:"product"})
|
||||
})
|
||||
galleryVes.value = true
|
||||
|
||||
})
|
||||
const show = (product) => {
|
||||
dialog.open(ProductDetails, {
|
||||
props: { modal: true, closable: true, header: t('productDetails') },
|
||||
data: {
|
||||
product
|
||||
}
|
||||
});
|
||||
}
|
||||
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
||||
|
||||
const store = useProductsStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
import { useProductsStore } from "@/stores/products";
|
||||
import { jDate } from "@/utils/jDate";
|
||||
import { numberFormat } from "@/utils/numberFormat";
|
||||
import {
|
||||
defineAsyncComponent,
|
||||
inject,
|
||||
onBeforeMount,
|
||||
ref,
|
||||
watchEffect,
|
||||
} from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
const ProductDetails = defineAsyncComponent(() =>
|
||||
import("../components/ProductDetails.vue")
|
||||
);
|
||||
|
||||
const edit = (data) =>{
|
||||
router.push(`/videos/${data._id}`)
|
||||
}
|
||||
|
||||
|
||||
const remove = (data) => {
|
||||
const { toast, dialog, confirm, t } = inject("service");
|
||||
|
||||
confirm.require({
|
||||
message: t('Are you sure you want to proceed?'),
|
||||
header: t('Danger Zone'),
|
||||
icon: 'pi pi-info-circle',
|
||||
acceptClass: 'p-button-danger p-button-sm',
|
||||
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
|
||||
rejectLabel: t('cancel'),
|
||||
acceptLabel: t('beOmitted'),
|
||||
defaultFocus: 'reject',
|
||||
accept: async () => {
|
||||
const deleted = await store.delete(data._id)
|
||||
const show = (product) => {
|
||||
dialog.open(ProductDetails, {
|
||||
props: { modal: true, closable: true, header: t("productDetails") },
|
||||
data: {
|
||||
product,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
await store.index()
|
||||
}
|
||||
// product.removing = true
|
||||
|
||||
// const { status, data } = await store.remove(product._id);
|
||||
|
||||
// product.removing = false
|
||||
|
||||
// if (status === 200)
|
||||
// toast.add({
|
||||
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
// });
|
||||
// else
|
||||
// toast.add({
|
||||
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
// });
|
||||
|
||||
// },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
watchEffect(async() => {
|
||||
const { page = 1, pageSize = 10 } = route.query
|
||||
await store.index(page, pageSize)
|
||||
const store = useProductsStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const edit = (data) => {
|
||||
router.push(`/videos/${data._id}`);
|
||||
};
|
||||
|
||||
const remove = (data) => {
|
||||
confirm.require({
|
||||
message: t("Are you sure you want to proceed?"),
|
||||
header: t("Danger Zone"),
|
||||
icon: "pi pi-info-circle",
|
||||
acceptClass: "p-button-danger p-button-sm",
|
||||
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
|
||||
rejectLabel: t("cancel"),
|
||||
acceptLabel: t("beOmitted"),
|
||||
defaultFocus: "reject",
|
||||
accept: async () => {
|
||||
const deleted = await store.delete(data._id);
|
||||
|
||||
await store.index();
|
||||
},
|
||||
// product.removing = true
|
||||
|
||||
// const { status, data } = await store.remove(product._id);
|
||||
|
||||
// product.removing = false
|
||||
|
||||
// if (status === 200)
|
||||
// toast.add({
|
||||
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
// });
|
||||
// else
|
||||
// toast.add({
|
||||
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
// });
|
||||
|
||||
// },
|
||||
});
|
||||
};
|
||||
|
||||
watchEffect(async () => {
|
||||
const { page = 1, pageSize = 10 } = route.query;
|
||||
await store.index(page, pageSize);
|
||||
console.log(store.items);
|
||||
});
|
||||
|
||||
const accepter = async (id, confirmToShow) => {
|
||||
const { status, data } = await store.confirm(id, { confirmToShow });
|
||||
|
||||
})
|
||||
|
||||
const accepter = async (id,confirmToShow)=>{
|
||||
const { status, data } = await store.confirm(id, {confirmToShow});
|
||||
|
||||
|
||||
if (status === 200)
|
||||
toast.add({
|
||||
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
||||
});
|
||||
toast.add({
|
||||
severity: "success",
|
||||
summary: t("successful"),
|
||||
detail: t("destroySuccessfully"),
|
||||
life: 3000,
|
||||
});
|
||||
else
|
||||
toast.add({
|
||||
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
||||
});
|
||||
}
|
||||
</script>
|
||||
toast.add({
|
||||
severity: "error",
|
||||
summary: t("error"),
|
||||
detail: data.msg,
|
||||
life: 3000,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -27,6 +27,6 @@ const store = useDiscountStore();
|
||||
import { jDate } from "@/utils/jDate";
|
||||
watchEffect(async () => {
|
||||
await store.history();
|
||||
console.log(store.items.discount);
|
||||
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user