441 lines
13 KiB
Vue
441 lines
13 KiB
Vue
<template>
|
|
<section class="flex flex-col gap-4">
|
|
<Panel :header="$t('createCourse')">
|
|
<div class="flex">
|
|
<div class="basis-4/6">
|
|
<div class="grid grid-cols-2 gap-7">
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputText
|
|
@change="setSlug"
|
|
class="w-full"
|
|
v-model="newCourse.name"
|
|
/>
|
|
<label>{{ $t("title") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<Dropdown
|
|
v-model="language"
|
|
@change="selectLanguage"
|
|
:options="['انگلیسی', 'فارسی']"
|
|
class="w-full"
|
|
/>
|
|
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
|
|
<label>{{ $t("زبان آموزش") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputText
|
|
v-model="newCourse.duration"
|
|
class="w-full"
|
|
prefix="ساعت"
|
|
/>
|
|
<label>{{ $t("مدت زمان دوره") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<Dropdown
|
|
v-model="level"
|
|
@change="selectLevel"
|
|
:options="['مبتدی', 'متوسط', 'پیشرفته']"
|
|
class="w-full"
|
|
/>
|
|
<label>{{ $t("سطح دوره") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputText
|
|
class="w-full"
|
|
v-model="newCourse.completionPercentage"
|
|
/>
|
|
<label>{{ $t("درصد تکمیل دوره") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputText class="w-full" v-model="newCourse.countOfVideos" />
|
|
<label>{{ $t("تعداد ویدیو ها") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputText class="w-full" v-model="newCourse.totalFileSize" />
|
|
<label>{{ $t("حجم کل دوره") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputGroup>
|
|
<InputText class="pointer-events-none truncate" dir="ltr" />
|
|
<Button severity="secondary" :loading="saving">
|
|
<i class="pi pi-image ml-2"></i>
|
|
<span>{{ $t("chooseImage") }}</span>
|
|
<input
|
|
type="file"
|
|
class="opacity-0 absolute inset-0 cursor-pointer"
|
|
@input="chooseImage"
|
|
/>
|
|
</Button>
|
|
</InputGroup>
|
|
<label>{{ $t("image") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
</div>
|
|
<div class="w-full mt-7">
|
|
<FloatLabel>
|
|
<Editor
|
|
v-model="newCourse.description"
|
|
placeholder="توضیحات"
|
|
editorStyle="height: 315px"
|
|
/>
|
|
</FloatLabel>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="basis-2/6 flex flex-col gap-7 ms-7">
|
|
<div class="flex flex-col gap-1">
|
|
<FloatLabel>
|
|
<InputText class="w-full" v-model="newCourse.price" />
|
|
<label>{{ $t("قیمت دوره") }}</label>
|
|
</FloatLabel>
|
|
</div>
|
|
|
|
<Listbox
|
|
listStyle="height:240px"
|
|
emptyMessage="دسته بندی وجود ندارد"
|
|
emptyFilterMessage="دسته بندی وجود ندارد"
|
|
v-model="selectedCategory"
|
|
@change="selectCategory"
|
|
filterPlaceholder=" دسته بندی"
|
|
:options="cities"
|
|
filter
|
|
optionLabel="name"
|
|
class="w-full"
|
|
/>
|
|
|
|
<Listbox
|
|
listStyle="height:240px"
|
|
emptyMessage="مدرس مورد نظر یافت نشد"
|
|
emptyFilterMessage="مدرس وجود ندارد"
|
|
v-model="selectedteacher"
|
|
@change="selectTeacher"
|
|
filterPlaceholder=" مدرس دوره"
|
|
:options="teachers"
|
|
filter
|
|
optionLabel="name"
|
|
class="w-full"
|
|
/>
|
|
</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
|
|
:label="$t('create')"
|
|
icon="pi pi-save"
|
|
severity="success"
|
|
:loading="creating"
|
|
@click="create(newCourse)"
|
|
/>
|
|
</div>
|
|
</Panel>
|
|
|
|
<Message
|
|
v-if="saving"
|
|
class="absolute right-12 bottom-8"
|
|
icon="pi pi-spinner-dotted"
|
|
severity="info"
|
|
>درحال آپلود تصویر...</Message
|
|
>
|
|
</section>
|
|
</template>
|
|
|
|
<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";
|
|
|
|
import { useStaticsStore } from "@/stores/statics";
|
|
import { useUploaderStore } from "@/stores/uploader";
|
|
import { useProductsStore } from "@/stores/products";
|
|
import { useCategoriesStore } from "@/stores/categoreis";
|
|
import { useUsersStore } from "../stores/users";
|
|
const categoriesStore = useCategoriesStore();
|
|
const courseStore = useProductsStore();
|
|
const uploadStore = useUploaderStore();
|
|
const userStore = useUsersStore();
|
|
import { useRoute, useRouter } from "vue-router";
|
|
const route = useRoute();
|
|
const level = ref(null);
|
|
const selectedCategory = ref();
|
|
const selectedteacher = ref();
|
|
const language = ref("");
|
|
const saving = ref(false);
|
|
const creating = ref(false);
|
|
const selectCategory = () => {
|
|
newCourse.categories = selectedCategory.value.id;
|
|
};
|
|
const selectTeacher = () => {
|
|
newCourse.teacher = selectedteacher.value.id;
|
|
};
|
|
watchEffect(async () => {
|
|
const { page = 1, rows = 10 } = route.query;
|
|
const categoriess = await categoriesStore.index(page, rows);
|
|
cities.value = categoriess;
|
|
});
|
|
watchEffect(async () => {
|
|
await userStore.index();
|
|
teachers.value = userStore.items.user;
|
|
});
|
|
|
|
const selectLevel = () => {
|
|
newCourse.level = level.value;
|
|
};
|
|
const selectLanguage = () => {
|
|
newCourse.language = language.value;
|
|
};
|
|
const selectedCity = ref();
|
|
const cities = ref();
|
|
const teachers = ref();
|
|
const levels = reactive(["آسان", "متوسط", "سخت"]);
|
|
const newCourse = reactive({
|
|
name: "",
|
|
description: "",
|
|
slug: "تست",
|
|
duration: "",
|
|
image: "",
|
|
categories: "",
|
|
price: null,
|
|
teacher: "",
|
|
completionPercentage: null,
|
|
level: "",
|
|
language: "",
|
|
countOfVideos: null,
|
|
totalFileSize: "",
|
|
});
|
|
const setSlug = () => {
|
|
newCourse.slug = newCourse.name;
|
|
};
|
|
const chooseImage = async (val) => {
|
|
saving.value = true;
|
|
const uploader = await uploadStore.uploader(val.target.files[0]);
|
|
newCourse.image = uploader.data.url;
|
|
saving.value = false;
|
|
if (uploader.status === 200 || uploader.status === 201) {
|
|
toast.add({
|
|
life: 2000,
|
|
severity: "success",
|
|
summary: t("successful"),
|
|
detail: t("تصویر آپلود شد"),
|
|
});
|
|
} else {
|
|
return toast.add({
|
|
life: 2000,
|
|
severity: "error",
|
|
summary: t("error"),
|
|
detail: t("خطا در بارگذاری تصویر، مجدد تلاش کنید"),
|
|
});
|
|
}
|
|
|
|
};
|
|
const router = useRouter();
|
|
const create = async (newCourse) => {
|
|
creating.value = true;
|
|
if (
|
|
newCourse.name === "" ||
|
|
newCourse.description === "" ||
|
|
newCourse.duration === "" ||
|
|
newCourse.categories === "" ||
|
|
newCourse.price === null ||
|
|
newCourse.teacher === "" ||
|
|
newCourse.completionPercentage === null ||
|
|
newCourse.level === "" ||
|
|
newCourse.language === "" ||
|
|
newCourse.countOfVideos === null ||
|
|
newCourse.totalFileSize === ""
|
|
) {
|
|
creating.value = false;
|
|
return toast.add({
|
|
life: 2000,
|
|
severity: "error",
|
|
summary: t("error"),
|
|
detail: t("تمام فیلد ها را وارد کنید"),
|
|
});
|
|
}
|
|
if (newCourse.image === "" || newCourse.image === null) {
|
|
creating.value = false;
|
|
return toast.add({
|
|
life: 2000,
|
|
severity: "error",
|
|
summary: t("error"),
|
|
detail: t("تصویر دوره را وارد کنید"),
|
|
});
|
|
}
|
|
const creat = await courseStore.create(newCourse);
|
|
|
|
|
|
if (creat.message === "دوره با موفقیت ثبت شد") {
|
|
toast.add({
|
|
life: 2000,
|
|
severity: "success",
|
|
summary: t("successful"),
|
|
detail: t("دوره جدید با موفقیت ایجاد شد"),
|
|
});
|
|
router.push('/courses')
|
|
creating.value = false;
|
|
} else {
|
|
creating.value = false;
|
|
toast.add({
|
|
life: 2000,
|
|
severity: "error",
|
|
summary: t("error"),
|
|
detail: t("مشکل در ایجاد دوره، مجدد تلاش کنید"),
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
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();
|
|
|
|
});
|
|
</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> --> |