deploy v-1
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
dist/
|
||||||
Vendored
+6
-8
@@ -7,20 +7,19 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
AddFaq: typeof import('./src/components/AddFaq.vue')['default']
|
||||||
AddVideo: typeof import('./src/components/AddVideo.vue')['default']
|
AddVideo: typeof import('./src/components/AddVideo.vue')['default']
|
||||||
Avatar: typeof import('primevue/avatar')['default']
|
Avatar: typeof import('primevue/avatar')['default']
|
||||||
Badge: typeof import('primevue/badge')['default']
|
|
||||||
Button: typeof import('primevue/button')['default']
|
Button: typeof import('primevue/button')['default']
|
||||||
Card: typeof import('primevue/card')['default']
|
Card: typeof import('primevue/card')['default']
|
||||||
CategoryForm: typeof import('./src/components/CategoryForm.vue')['default']
|
CategoryForm: typeof import('./src/components/CategoryForm.vue')['default']
|
||||||
Checkbox: typeof import('primevue/checkbox')['default']
|
|
||||||
Column: typeof import('primevue/column')['default']
|
Column: typeof import('primevue/column')['default']
|
||||||
copy: typeof import('./src/components/ProductDetails copy.vue')['default']
|
|
||||||
CouponDetails: typeof import('./src/components/CouponDetails.vue')['default']
|
CouponDetails: typeof import('./src/components/CouponDetails.vue')['default']
|
||||||
DataTable: typeof import('primevue/datatable')['default']
|
DataTable: typeof import('primevue/datatable')['default']
|
||||||
Dropdown: typeof import('primevue/dropdown')['default']
|
Dropdown: typeof import('primevue/dropdown')['default']
|
||||||
Editor: typeof import('primevue/editor')['default']
|
Editor: typeof import('primevue/editor')['default']
|
||||||
Fieldset: typeof import('primevue/fieldset')['default']
|
EditVideo: typeof import('./src/components/EditVideo.vue')['default']
|
||||||
|
FaqDetails: typeof import('./src/components/FaqDetails.vue')['default']
|
||||||
FileUpload: typeof import('primevue/fileupload')['default']
|
FileUpload: typeof import('primevue/fileupload')['default']
|
||||||
FirstStep: typeof import('./src/components/ProductFormSteps/FirstStep.vue')['default']
|
FirstStep: typeof import('./src/components/ProductFormSteps/FirstStep.vue')['default']
|
||||||
FloatLabel: typeof import('primevue/floatlabel')['default']
|
FloatLabel: typeof import('primevue/floatlabel')['default']
|
||||||
@@ -36,26 +35,25 @@ declare module 'vue' {
|
|||||||
InputText: typeof import('primevue/inputtext')['default']
|
InputText: typeof import('primevue/inputtext')['default']
|
||||||
Listbox: typeof import('primevue/listbox')['default']
|
Listbox: typeof import('primevue/listbox')['default']
|
||||||
Menu: typeof import('primevue/menu')['default']
|
Menu: typeof import('primevue/menu')['default']
|
||||||
|
Message: typeof import('primevue/message')['default']
|
||||||
OverlayPanel: typeof import('primevue/overlaypanel')['default']
|
OverlayPanel: typeof import('primevue/overlaypanel')['default']
|
||||||
Pagination: typeof import('./src/components/Pagination.vue')['default']
|
Pagination: typeof import('./src/components/Pagination.vue')['default']
|
||||||
Paginator: typeof import('primevue/paginator')['default']
|
Paginator: typeof import('primevue/paginator')['default']
|
||||||
Panel: typeof import('primevue/panel')['default']
|
Panel: typeof import('primevue/panel')['default']
|
||||||
PanelMenu: typeof import('primevue/panelmenu')['default']
|
PanelMenu: typeof import('primevue/panelmenu')['default']
|
||||||
Password: typeof import('primevue/password')['default']
|
|
||||||
ProductDetails: typeof import('./src/components/ProductDetails.vue')['default']
|
ProductDetails: typeof import('./src/components/ProductDetails.vue')['default']
|
||||||
ProductForm: typeof import('./src/components/ProductForm.vue')['default']
|
ProductForm: typeof import('./src/components/ProductForm.vue')['default']
|
||||||
ProgressBar: typeof import('primevue/progressbar')['default']
|
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SecondStep: typeof import('./src/components/ProductFormSteps/SecondStep.vue')['default']
|
SecondStep: typeof import('./src/components/ProductFormSteps/SecondStep.vue')['default']
|
||||||
Sidebar: typeof import('primevue/sidebar')['default']
|
Sidebar: typeof import('primevue/sidebar')['default']
|
||||||
|
Stepper: typeof import('primevue/stepper')['default']
|
||||||
|
StepperPanel: typeof import('primevue/stepperpanel')['default']
|
||||||
Tag: typeof import('primevue/tag')['default']
|
Tag: typeof import('primevue/tag')['default']
|
||||||
Textarea: typeof import('primevue/textarea')['default']
|
Textarea: typeof import('primevue/textarea')['default']
|
||||||
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
|
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
|
||||||
ThirdStep: typeof import('./src/components/ProductFormSteps/ThirdStep.vue')['default']
|
ThirdStep: typeof import('./src/components/ProductFormSteps/ThirdStep.vue')['default']
|
||||||
ToggleButton: typeof import('primevue/togglebutton')['default']
|
|
||||||
Toolbar: typeof import('primevue/toolbar')['default']
|
Toolbar: typeof import('primevue/toolbar')['default']
|
||||||
TreeTable: typeof import('primevue/treetable')['default']
|
|
||||||
UserDetails: typeof import('./src/components/UserDetails.vue')['default']
|
UserDetails: typeof import('./src/components/UserDetails.vue')['default']
|
||||||
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
|
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,73 @@
|
|||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="flex flex-col gap-4 pt-7">
|
||||||
|
<div class="flex gap-7">
|
||||||
|
<div class="flex flex-col gap-7 items-center">
|
||||||
|
<div class="flex flex-col gap-1 w-96">
|
||||||
|
<FloatLabel>
|
||||||
|
<InputText class="w-full" v-model="newFaq.title" />
|
||||||
|
<label>{{ $t("سوال") }}</label>
|
||||||
|
</FloatLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1 w-96">
|
||||||
|
<FloatLabel>
|
||||||
|
<Textarea v-model="newFaq.description" rows="3" class="w-full" />
|
||||||
|
<label>{{ $t("پاسخ") }}</label>
|
||||||
|
</FloatLabel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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(newFaq)"
|
||||||
|
:loading="saving"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
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 newFaq = reactive({
|
||||||
|
title: "",
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const save = async (newFaq) => {
|
||||||
|
saving.value = true;
|
||||||
|
const send = await store.create(newFaq);
|
||||||
|
if (send.status === 200 || send.status === 201) {
|
||||||
|
toast.add({
|
||||||
|
severity: "success",
|
||||||
|
summary: "موفق",
|
||||||
|
detail: "آیتم جدید ایجاد شد",
|
||||||
|
life: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.value.close()
|
||||||
|
}
|
||||||
|
saving.value = false;
|
||||||
|
store.index()
|
||||||
|
};
|
||||||
|
</script>
|
||||||
+53
-181
@@ -5,15 +5,15 @@
|
|||||||
<div class="flex flex-col gap-7 items-center">
|
<div class="flex flex-col gap-7 items-center">
|
||||||
<div class="flex flex-col gap-1 w-96">
|
<div class="flex flex-col gap-1 w-96">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="headline"/>
|
<InputText class="w-full" v-model="newEpisode.headline"/>
|
||||||
<label>{{ $t("عنوان ویدیو") }}</label>
|
<label>{{ $t("عنوان ویدیو") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1 w-96">
|
<div class="flex flex-col gap-1 w-96">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<Textarea v-model="description" rows="3" class="w-full" />
|
<Textarea v-model="newEpisode.description" rows="3" class="w-full" />
|
||||||
<label>{{ $t("توضیحات متا") }}</label>
|
<label>{{ $t("توضیحات") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -33,166 +33,33 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-7 items-center">
|
<div class="flex flex-col gap-7 items-center">
|
||||||
<div>
|
<FileUpload mode="advanced" multiple @select="chooseImages"
|
||||||
<div class="card w-[400px]">
|
@remove="chooseImages" >
|
||||||
<Toast />
|
<template #header="{ chooseCallback }">
|
||||||
<FileUpload
|
<div class="flex justify-between w-full items-center">
|
||||||
name="demo[]"
|
<h2 class="font-bold text-xl ">{{ $t('بارگذاری ویدیو') }}</h2>
|
||||||
url="/api/upload"
|
<Button :label="$t('انتخاب ویدیو')" severity="secondary" icon="pi pi-video" text
|
||||||
@upload="onTemplatedUpload($event)"
|
@click="chooseCallback()" />
|
||||||
:multiple="true"
|
|
||||||
accept="image/*"
|
|
||||||
:maxFileSize="1000000"
|
|
||||||
@select="onSelectedFiles"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
#header="{
|
|
||||||
chooseCallback,
|
|
||||||
uploadCallback,
|
|
||||||
clearCallback,
|
|
||||||
files,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="flex flex-wrap justify-between items-center flex-1 gap-4"
|
|
||||||
>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<Button
|
|
||||||
@click="chooseCallback()"
|
|
||||||
icon="pi pi-images"
|
|
||||||
rounded
|
|
||||||
outlined
|
|
||||||
severity="secondary"
|
|
||||||
></Button>
|
|
||||||
<Button
|
|
||||||
@click="uploadEvent(uploadCallback)"
|
|
||||||
icon="pi pi-cloud-upload"
|
|
||||||
rounded
|
|
||||||
outlined
|
|
||||||
severity="success"
|
|
||||||
:disabled="!files || files.length === 0"
|
|
||||||
></Button>
|
|
||||||
<Button
|
|
||||||
@click="clearCallback()"
|
|
||||||
icon="pi pi-times"
|
|
||||||
rounded
|
|
||||||
outlined
|
|
||||||
severity="danger"
|
|
||||||
:disabled="!files || files.length === 0"
|
|
||||||
></Button>
|
|
||||||
</div>
|
|
||||||
<ProgressBar
|
|
||||||
:value="totalSizePercent"
|
|
||||||
:showValue="false"
|
|
||||||
class="md:w-20rem h-1 w-full md:ml-auto"
|
|
||||||
>
|
|
||||||
<span class="whitespace-nowrap"
|
|
||||||
>{{ totalSize }}B / 1Mb</span
|
|
||||||
>
|
|
||||||
</ProgressBar>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template #content="{ files }">
|
||||||
#content="{
|
<div v-if="files.length > 0" class="flex gap-4 justify-between ">
|
||||||
files,
|
<div v-if="loading" class="h-[300px] w-[500px] flex justify-center items-center">درحال بارگذاری ویدیو</div>
|
||||||
uploadedFiles,
|
<div v-else class="h-[300px] w-[500px] flex justify-center items-center">ویدیو آپلود شد</div>
|
||||||
removeUploadedFileCallback,
|
|
||||||
removeFileCallback,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col gap-8 pt-4">
|
|
||||||
<div v-if="files.length > 0">
|
|
||||||
<h5>Pending</h5>
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
<div
|
|
||||||
v-for="(file, index) of files"
|
|
||||||
:key="file.name + file.type + file.size"
|
|
||||||
class="p-8 rounded-border flex flex-col border border-surface items-center gap-4"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
role="presentation"
|
|
||||||
:alt="file.name"
|
|
||||||
:src="file.objectURL"
|
|
||||||
width="100"
|
|
||||||
height="50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden"
|
|
||||||
>{{ file.name }}</span
|
|
||||||
>
|
|
||||||
<div>{{ formatSize(file.size) }}</div>
|
|
||||||
<Badge value="Pending" severity="warn" />
|
|
||||||
<Button
|
|
||||||
icon="pi pi-times"
|
|
||||||
@click="
|
|
||||||
onRemoveTemplatingFile(
|
|
||||||
file,
|
|
||||||
removeFileCallback,
|
|
||||||
index
|
|
||||||
)
|
|
||||||
"
|
|
||||||
outlined
|
|
||||||
rounded
|
|
||||||
severity="danger"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div v-if="uploadedFiles.length > 0">
|
|
||||||
<h5>Completed</h5>
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
<div
|
|
||||||
v-for="(file, index) of uploadedFiles"
|
|
||||||
:key="file.name + file.type + file.size"
|
|
||||||
class="p-8 rounded-border flex flex-col border border-surface items-center gap-4"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
role="presentation"
|
|
||||||
:alt="file.name"
|
|
||||||
:src="file.objectURL"
|
|
||||||
width="100"
|
|
||||||
height="50"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden"
|
|
||||||
>{{ file.name }}</span
|
|
||||||
>
|
|
||||||
<div>{{ formatSize(file.size) }}</div>
|
|
||||||
<Badge
|
|
||||||
value="Completed"
|
|
||||||
class="mt-4"
|
|
||||||
severity="success"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
icon="pi pi-times"
|
|
||||||
@click="removeUploadedFileCallback(index)"
|
|
||||||
outlined
|
|
||||||
rounded
|
|
||||||
severity="danger"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<div class="flex items-center justify-center flex-col">
|
<div class="flex items-center justify-center flex-col gap-8 py-12 w-[51rem] h-96">
|
||||||
<i
|
<i class="pi pi-video border-2 rounded-full p-5 text-8xl text-400 border-400" />
|
||||||
class="pi pi-cloud-upload border-2 rounded-full p-8 text-4xl text-muted-color"
|
<p>{{ $t('جهت آپلود ویدیو را اینجا رها کنید') }}</p>
|
||||||
/>
|
|
||||||
<p class="mt-6 mb-0">ویدیو را اینجا رها کنید</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</FileUpload>
|
</FileUpload>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w-full flex gap-2 mt-4">
|
<div class="w-full flex gap-2 mt-4">
|
||||||
<Button
|
<Button
|
||||||
@@ -218,56 +85,61 @@ import { inject, reactive, ref } from "vue";
|
|||||||
import { usePrimeVue } from "primevue/config";
|
import { usePrimeVue } from "primevue/config";
|
||||||
import { useToast } from "primevue/usetoast";
|
import { useToast } from "primevue/usetoast";
|
||||||
import { useHeadlinesStore } from "@/stores/Headlines"
|
import { useHeadlinesStore } from "@/stores/Headlines"
|
||||||
|
import { useUploaderStore } from "@/stores/uploader";
|
||||||
|
const loading = ref(false)
|
||||||
|
const uploadStore = useUploaderStore();
|
||||||
|
|
||||||
|
|
||||||
const store = useHeadlinesStore()
|
const store = useHeadlinesStore()
|
||||||
const dialog = inject("dialogRef");
|
const dialog = inject("dialogRef");
|
||||||
const type = ref();
|
const type = ref();
|
||||||
const produc = reactive(Object.assign({}, dialog.value.data));
|
const produc = reactive(dialog.value.data);
|
||||||
const sabt = () => {
|
const saving = ref(false)
|
||||||
console.log(produc);
|
|
||||||
};
|
|
||||||
const videoStatus = ref(null);
|
const videoStatus = ref(null);
|
||||||
const setType = () => {
|
const setType = () => {
|
||||||
if (type.value === "غیرفعال") {
|
if (type.value === "غیرفعال") {
|
||||||
videoStatus.value = "DeActivate";
|
newEpisode.status = "DeActivate";
|
||||||
}
|
}
|
||||||
if (type.value === "تکمیل شده") {
|
if (type.value === "تکمیل شده") {
|
||||||
videoStatus.value = "Completed";
|
newEpisode.status = "Completed";
|
||||||
}
|
}
|
||||||
if (type.value === "درحال ضبط") {
|
if (type.value === "درحال ضبط") {
|
||||||
videoStatus.value = "Recording";
|
newEpisode.status = "Recording";
|
||||||
}
|
}
|
||||||
if (type.value === "به زودی...") {
|
if (type.value === "به زودی...") {
|
||||||
videoStatus.value = "Soon";
|
newEpisode.status = "Soon";
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
const newEpisode = reactive({
|
||||||
|
headline: "",
|
||||||
|
description: "",
|
||||||
|
course: produc.produc,
|
||||||
|
videoUrl: "",
|
||||||
|
status: ""
|
||||||
|
})
|
||||||
|
|
||||||
|
const chooseImages = async(files) => {
|
||||||
|
loading.value = true
|
||||||
|
const uploader = await uploadStore.uploader(files.files[0]);
|
||||||
|
newEpisode.videoUrl = uploader.data.key
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
}
|
}
|
||||||
console.log("type:", videoStatus.value, produc.produc);
|
|
||||||
console.log(type.value);
|
|
||||||
};
|
|
||||||
const headline = ref('');
|
|
||||||
const description = ref('');
|
|
||||||
const videoUrl = ref('');
|
|
||||||
const $primevue = usePrimeVue();
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const totalSize = ref(0);
|
|
||||||
const totalSizePercent = ref(0);
|
|
||||||
const files = ref([]);
|
|
||||||
const course = ref(produc.produc)
|
|
||||||
|
|
||||||
const save = async() =>{
|
const save = async() =>{
|
||||||
const send = await store.create({
|
saving.value = true;
|
||||||
headline: headline.value,
|
const send = await store.create(newEpisode)
|
||||||
description: description.value,
|
saving.value = false;
|
||||||
course: produc.produc,
|
dialog.value.close()
|
||||||
videoUrl: 'https://dlearn.storage.iran.liara.space/test/1cb985c8-26a0-4120-824c-f496349e45bfmatt_devir--one_minute_drive_preview.webm',
|
|
||||||
status: videoStatus.value
|
|
||||||
})
|
|
||||||
console.log('send', send);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
|
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
|
||||||
removeFileCallback(index);
|
removeFileCallback(index);
|
||||||
totalSize.value -= parseInt(formatSize(file.size));
|
totalSize.value -= parseInt(formatSize(file.size));
|
||||||
totalSizePercent.value = totalSize.value / 10;
|
totalSizePercent.value = totalSize.value / 10;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClearTemplatingUpload = (clear) => {
|
const onClearTemplatingUpload = (clear) => {
|
||||||
|
|||||||
@@ -50,8 +50,8 @@
|
|||||||
const save = async () => {
|
const save = async () => {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
const { status, data } = await store.create(name)
|
const { status, data } = await store.create(name)
|
||||||
console.log('status',status);
|
|
||||||
save.value = false
|
saving.value = false
|
||||||
|
|
||||||
if (status === 201 || status === 200)
|
if (status === 201 || status === 200)
|
||||||
toast.add({
|
toast.add({
|
||||||
|
|||||||
@@ -37,8 +37,11 @@
|
|||||||
</li>
|
</li>
|
||||||
<li class="flex items-center justify-between">
|
<li class="flex items-center justify-between">
|
||||||
<strong>{{ $t('تغییر وضعیت') }}</strong>
|
<strong>{{ $t('تغییر وضعیت') }}</strong>
|
||||||
<ToggleButton @change="changeStatus" v-model="checked" onLabel="فعال" offLabel="غیرفعال" />
|
<!-- <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>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -51,13 +54,57 @@ import { numberFormat } from '@/utils/numberFormat';
|
|||||||
import {useDiscountStore} from '../stores/discounts'
|
import {useDiscountStore} from '../stores/discounts'
|
||||||
const store = useDiscountStore()
|
const store = useDiscountStore()
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
const checked = ref(false);
|
const checked = ref(false);
|
||||||
const changeStatus = async() => {
|
const { confirm, toast, t } = inject("service");
|
||||||
|
const router = useRouter()
|
||||||
|
const saving = ref(false)
|
||||||
|
const changeToDeActive = async() =>{
|
||||||
|
const change = await store.changeStatus({
|
||||||
|
id: coupon.coupon.id,
|
||||||
|
status: 'DeActivate'
|
||||||
|
})
|
||||||
|
if (change === true) {
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("وضعیت ویرایش شد"),
|
||||||
|
});
|
||||||
|
store.index()
|
||||||
|
dialog.value.close()
|
||||||
|
}else{
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("خطا، محدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
const changeToActive = async() => {
|
||||||
const change = await store.changeStatus({
|
const change = await store.changeStatus({
|
||||||
id: coupon.coupon.id,
|
id: coupon.coupon.id,
|
||||||
status: 'Active'
|
status: 'Active'
|
||||||
})
|
})
|
||||||
console.log(change);
|
if (change === true) {
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("وضعیت ویرایش شد"),
|
||||||
|
});
|
||||||
|
store.index()
|
||||||
|
dialog.value.close()
|
||||||
|
}else{
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("خطا، محدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
const tabs = ref([
|
const tabs = ref([
|
||||||
@@ -68,7 +115,7 @@ const changeStatus = async() => {
|
|||||||
const dialog = inject('dialogRef')
|
const dialog = inject('dialogRef')
|
||||||
|
|
||||||
const coupon = reactive(Object.assign({}, dialog.value.data))
|
const coupon = reactive(Object.assign({}, dialog.value.data))
|
||||||
console.log('der', coupon.coupon);
|
|
||||||
const confirmToSales = (value) => {
|
const confirmToSales = (value) => {
|
||||||
dialog.value.data.confirm(user, value)
|
dialog.value.data.confirm(user, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="flex flex-col gap-4 pt-7">
|
||||||
|
<div class="flex gap-7">
|
||||||
|
<div class="flex flex-col gap-7 items-center">
|
||||||
|
<div class="flex flex-col gap-1 w-96">
|
||||||
|
<FloatLabel>
|
||||||
|
<InputText class="w-full" v-model="newEpisode.headline"/>
|
||||||
|
<label>{{ $t("عنوان ویدیو") }}</label>
|
||||||
|
</FloatLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1 w-96">
|
||||||
|
<FloatLabel>
|
||||||
|
<Textarea v-model="newEpisode.description" rows="3" class="w-full" />
|
||||||
|
<label>{{ $t("توضیحات") }}</label>
|
||||||
|
</FloatLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1 w-96">
|
||||||
|
<FloatLabel>
|
||||||
|
<Dropdown
|
||||||
|
v-model="type"
|
||||||
|
@change="setType"
|
||||||
|
:options="['تکمیل شده', 'غیرفعال', 'درحال ضبط', 'به زودی...']"
|
||||||
|
:optionLabel="levels"
|
||||||
|
:optionValue="levels"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
<label>{{ $t("وضعیت ویدیو") }}</label>
|
||||||
|
</FloatLabel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-7 items-center">
|
||||||
|
<FileUpload mode="advanced" multiple @select="chooseImages"
|
||||||
|
@remove="chooseImages" >
|
||||||
|
<template #header="{ chooseCallback }">
|
||||||
|
<div class="flex justify-between w-full items-center">
|
||||||
|
<h2 class="font-bold text-xl ">{{ $t('بارگذاری ویدیو') }}</h2>
|
||||||
|
<Button :label="$t('انتخاب ویدیو')" severity="secondary" icon="pi pi-video" text
|
||||||
|
@click="chooseCallback()" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #content="{ files }">
|
||||||
|
<div v-if="files.length > 0" class="flex gap-4 justify-between ">
|
||||||
|
<div v-if="loading" class="h-[300px] w-[500px] flex justify-center items-center">درحال بارگذاری ویدیو</div>
|
||||||
|
<div v-else class="h-[300px] w-[500px] flex justify-center items-center">ویدیو آپلود شد</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template #empty>
|
||||||
|
<div class="flex items-center justify-center flex-col gap-8 py-12 w-[51rem] h-96">
|
||||||
|
<i class="pi pi-video border-2 rounded-full p-5 text-8xl text-400 border-400" />
|
||||||
|
<p>{{ $t('جهت تغییر ویدیو، ویدیو جدید را اینجا رها کنید') }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FileUpload>
|
||||||
|
</div>
|
||||||
|
</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 { 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 type = ref();
|
||||||
|
const produc = reactive(dialog.value.data);
|
||||||
|
|
||||||
|
|
||||||
|
const videoStatus = ref(null);
|
||||||
|
const setType = () => {
|
||||||
|
if (type.value === "غیرفعال") {
|
||||||
|
newEpisode.status = "DeActivate";
|
||||||
|
}
|
||||||
|
if (type.value === "تکمیل شده") {
|
||||||
|
newEpisode.status = "Completed";
|
||||||
|
}
|
||||||
|
if (type.value === "درحال ضبط") {
|
||||||
|
newEpisode.status = "Recording";
|
||||||
|
}
|
||||||
|
if (type.value === "به زودی...") {
|
||||||
|
newEpisode.status = "Soon";
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const newEpisode = reactive({
|
||||||
|
headline: produc.product.headline,
|
||||||
|
description: produc.product.description,
|
||||||
|
course: produc.product.course,
|
||||||
|
videoUrl: produc.product.videoUrl,
|
||||||
|
status: produc.product.status
|
||||||
|
})
|
||||||
|
watchEffect(() =>{
|
||||||
|
if (produc.product.status === "Completed") {
|
||||||
|
type.value = 'تکمیل شده'
|
||||||
|
}
|
||||||
|
if (produc.product.status === "DeActivate") {
|
||||||
|
type.value = 'غیر فعال'
|
||||||
|
}
|
||||||
|
if (produc.product.status === "Recording") {
|
||||||
|
type.value = "درحال ضبط"
|
||||||
|
}
|
||||||
|
if (produc.product.status === "Soon") {
|
||||||
|
type.value = "به زودی..."
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const chooseImages = async(files) => {
|
||||||
|
loading.value = true
|
||||||
|
const uploader = await uploadStore.uploader(files.files[0]);
|
||||||
|
newEpisode.videoUrl = uploader.data.key
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const save = async() =>{
|
||||||
|
saving.value = true
|
||||||
|
const send = await store.edit(produc.product.id, newEpisode)
|
||||||
|
console.log(send);
|
||||||
|
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>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<section class="flex flex-col gap-6 w-[700px]">
|
||||||
|
|
||||||
|
<ul class="flex flex-col gap-5 space-y-6">
|
||||||
|
|
||||||
|
<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.title }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center justify-between gap-32">
|
||||||
|
<strong>{{ $t('پاسخ') }}</strong>
|
||||||
|
<span>{{ user.product.description }}</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')
|
||||||
|
|
||||||
|
const user = reactive(Object.assign({}, dialog.value.data))
|
||||||
|
|
||||||
|
const confirmToSales = (value) => {
|
||||||
|
dialog.value.data.confirm(user, value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<ul class="flex flex-col gap-5">
|
<ul class="flex flex-col gap-5">
|
||||||
|
|
||||||
<li class="flex items-center justify-between">
|
<li class="flex items-center justify-between">
|
||||||
<video :src="head.product.videoUrl" autoplay></video>
|
<video :src="videoUrl.data.url" autoplay></video>
|
||||||
</li>
|
</li>
|
||||||
<li class="flex items-center justify-between">
|
<li class="flex items-center justify-between">
|
||||||
<strong>{{ $t('عنوان') }}</strong>
|
<strong>{{ $t('عنوان') }}</strong>
|
||||||
@@ -63,20 +63,26 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { inject, reactive } from 'vue';
|
import { inject, reactive, watchEffect } from 'vue';
|
||||||
import { jDate } from '@/utils/jDate';
|
import { jDate } from '@/utils/jDate';
|
||||||
import { numberFormat } from '@/utils/numberFormat';
|
import { numberFormat } from '@/utils/numberFormat';
|
||||||
import { ref } from 'vue';
|
import { useUploaderStore } from "@/stores/uploader"
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
const store = useUploaderStore()
|
||||||
const tabs = ref([
|
const tabs = ref([
|
||||||
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
|
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
|
||||||
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
|
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
|
||||||
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
|
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
|
||||||
]);
|
]);
|
||||||
const dialog = inject('dialogRef')
|
const dialog = inject('dialogRef')
|
||||||
|
const videoUrl = ref()
|
||||||
const head = reactive(Object.assign({}, dialog.value.data))
|
const head = reactive(Object.assign({}, dialog.value.data))
|
||||||
console.log('der', head);
|
console.log('head', );
|
||||||
|
watchEffect(async() =>{
|
||||||
|
videoUrl.value = await store.getUrl(head.product.videoUrl)
|
||||||
|
console.log(videoUrl);
|
||||||
|
})
|
||||||
// const confirmToSales = (value) => {
|
// const confirmToSales = (value) => {
|
||||||
// dialog.value.data.confirm(user, value)
|
// dialog.value.data.confirm(user, value)
|
||||||
// }
|
// }
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ import { ref } from 'vue';
|
|||||||
const dialog = inject('dialogRef')
|
const dialog = inject('dialogRef')
|
||||||
|
|
||||||
const user = reactive(Object.assign({}, dialog.value.data))
|
const user = reactive(Object.assign({}, dialog.value.data))
|
||||||
console.log('der', user.product);
|
|
||||||
const confirmToSales = (value) => {
|
const confirmToSales = (value) => {
|
||||||
dialog.value.data.confirm(user, value)
|
dialog.value.data.confirm(user, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const { toast, t } = inject('service')
|
|||||||
const { product } = dialog.value.data
|
const { product } = dialog.value.data
|
||||||
|
|
||||||
const form = reactive(Object.assign({}, product || {}))
|
const form = reactive(Object.assign({}, product || {}))
|
||||||
console.log(form)
|
|
||||||
const errors = reactive({})
|
const errors = reactive({})
|
||||||
|
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
@@ -48,7 +48,6 @@ const uploader = useUploaderStore()
|
|||||||
const save = async () => {
|
const save = async () => {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
|
|
||||||
console.log(form);
|
|
||||||
|
|
||||||
//Upload File
|
//Upload File
|
||||||
if (typeof form.coverImage === 'object') {
|
if (typeof form.coverImage === 'object') {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ import { inject } from 'vue';
|
|||||||
const dialogRef = inject('dialogRef')
|
const dialogRef = inject('dialogRef')
|
||||||
|
|
||||||
const { form, errors } = inject('data')
|
const { form, errors } = inject('data')
|
||||||
console.log('ax',form);
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,12 @@
|
|||||||
<strong>{{ $t('status') }}</strong>
|
<strong>{{ $t('status') }}</strong>
|
||||||
<span>{{ user.statusText }}</span>
|
<span>{{ user.statusText }}</span>
|
||||||
</li>
|
</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>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -35,12 +40,64 @@ import { jDate } from '@/utils/jDate';
|
|||||||
import { inject, reactive } from 'vue';
|
import { inject, reactive } from 'vue';
|
||||||
|
|
||||||
const dialog = inject('dialogRef')
|
const dialog = inject('dialogRef')
|
||||||
|
import { useUsersStore } from '@/stores/users';
|
||||||
|
const store = useUsersStore()
|
||||||
const user = reactive(Object.assign({}, dialog.value.data.user))
|
const user = reactive(Object.assign({}, dialog.value.data.user))
|
||||||
|
const { confirm, toast, t } = inject("service");
|
||||||
const confirmToSales = (value) => {
|
const confirmToSales = (value) => {
|
||||||
dialog.value.data.confirm(user, value)
|
dialog.value.data.confirm(user, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeToDeActive = async() =>{
|
||||||
|
const change = await store.changeStatus({
|
||||||
|
userId: user.id,
|
||||||
|
status: 'DeActivate'
|
||||||
|
})
|
||||||
|
if (change === true) {
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("وضعیت ویرایش شد"),
|
||||||
|
});
|
||||||
|
store.index()
|
||||||
|
dialog.value.close()
|
||||||
|
}else{
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("خطا، محدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
const changeToActive = async() => {
|
||||||
|
const change = await store.changeStatus({
|
||||||
|
userId: user.id,
|
||||||
|
status: 'Active'
|
||||||
|
})
|
||||||
|
if (change === true) {
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("وضعیت ویرایش شد"),
|
||||||
|
});
|
||||||
|
store.index()
|
||||||
|
dialog.value.close()
|
||||||
|
}else{
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("خطا، محدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
@@ -128,7 +128,7 @@ const items = ref([
|
|||||||
{
|
{
|
||||||
label: t('تاریخچه ی تخفیف ها'),
|
label: t('تاریخچه ی تخفیف ها'),
|
||||||
icon: 'pi pi-history',
|
icon: 'pi pi-history',
|
||||||
command: () => router.push('/Coupons')
|
command: () => router.push('/couponsHistory')
|
||||||
},
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useAdminsStore } from '@/stores/admins'
|
import { useAdminsStore } from '@/stores/admins'
|
||||||
import axioss from 'axios'
|
import axioss from 'axios'
|
||||||
|
|
||||||
const baseURL = 'https://dlearn.iran.liara.run'
|
const baseURL = 'https://dlearn.liara.run'
|
||||||
|
|
||||||
const axios = axioss.create({ baseURL })
|
const axios = axioss.create({ baseURL })
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -14,8 +14,8 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Main',
|
// name: 'Main',
|
||||||
//redirect: '/products',
|
redirect: '/courses',
|
||||||
component: Main,
|
component: Main,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -108,6 +108,11 @@ const router = createRouter({
|
|||||||
name: 'AddVideos',
|
name: 'AddVideos',
|
||||||
component: () => import('@/views/AddVideos.vue'),
|
component: () => import('@/views/AddVideos.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/couponsHistory',
|
||||||
|
name: 'couponsHistory',
|
||||||
|
component: () => import('@/views/couponsHistory.vue'),
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// path: '/videos/create',
|
// path: '/videos/create',
|
||||||
// name: 'createVideo',
|
// name: 'createVideo',
|
||||||
|
|||||||
@@ -30,10 +30,9 @@ export const useHeadlinesStore = defineStore('headlines', {
|
|||||||
// },
|
// },
|
||||||
|
|
||||||
|
|
||||||
async edit(id) {
|
async edit(id, body) {
|
||||||
const { status, data } = await axios.get(`/course/${id}`)
|
const { status, data } = await axios.put(`/courseHeadline/${id}`, body)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
},
|
},
|
||||||
async create(form){
|
async create(form){
|
||||||
const {status, data} = await axios.post('/courseHeadline', form)
|
const {status, data} = await axios.post('/courseHeadline', form)
|
||||||
|
|||||||
@@ -19,11 +19,8 @@ export const useAdminsStore = defineStore('admins', {
|
|||||||
phone: number.value,
|
phone: number.value,
|
||||||
type: 1
|
type: 1
|
||||||
})
|
})
|
||||||
console.log('state',data);
|
|
||||||
if (status == 200) {
|
if (status == 200) {
|
||||||
// this.token = data.accessToken
|
|
||||||
// this.permissions = data.permissions
|
|
||||||
// console.log(data);
|
|
||||||
|
|
||||||
return { status, data, error }
|
return { status, data, error }
|
||||||
}
|
}
|
||||||
@@ -33,13 +30,15 @@ export const useAdminsStore = defineStore('admins', {
|
|||||||
phone: number.value,
|
phone: number.value,
|
||||||
code: code.value
|
code: code.value
|
||||||
})
|
})
|
||||||
|
console.log('data server', data);
|
||||||
console.log(data);
|
|
||||||
if (status == 200) {
|
if (status == 200) {
|
||||||
console.log('first', this.token);
|
if (data.roles[0] === "Admin") {
|
||||||
this.token = data.token
|
this.token = data.token
|
||||||
// this.permissions = data.permissions
|
}
|
||||||
console.log('two', this.token);
|
else{
|
||||||
|
this.token = ''
|
||||||
|
}
|
||||||
|
|
||||||
return { status, data, error }
|
return { status, data, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,10 +46,9 @@ export const useCategoriesStore = defineStore('categories', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async index(page, rows) {
|
async index() {
|
||||||
this.fetching = true
|
this.fetching = true
|
||||||
const params = { page, rows }
|
const {data} = await axios.get('/courseCategory')
|
||||||
const { status, data } = await axios.get('/courseCategory', { params })
|
|
||||||
this.fetching = false
|
this.fetching = false
|
||||||
return data
|
return data
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
@@ -81,7 +80,7 @@ export const useCategoriesStore = defineStore('categories', {
|
|||||||
name: id.value
|
name: id.value
|
||||||
})
|
})
|
||||||
|
|
||||||
//console.log('result', result);
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const useCommentStore = defineStore('comments', {
|
|||||||
this.fetching = true
|
this.fetching = true
|
||||||
const data = await axios.post(`/course/${id}/comment`, body)
|
const data = await axios.post(`/course/${id}/comment`, body)
|
||||||
this.fetching = false
|
this.fetching = false
|
||||||
console.log(id, 'id');
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ export const useCommentStore = defineStore('comments', {
|
|||||||
name: id.value
|
name: id.value
|
||||||
})
|
})
|
||||||
|
|
||||||
//console.log('result', result);
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+27
-6
@@ -10,6 +10,32 @@ export const useDiscountStore = defineStore('discounts', {
|
|||||||
total: 0
|
total: 0
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
async add(form) {
|
||||||
|
this.fetching = true
|
||||||
|
const { status, data } = await axios.post('/admins/discount', form)
|
||||||
|
this.fetching = false
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
async history() {
|
||||||
|
this.fetching = true
|
||||||
|
const { status, data } = await axios.get('/admins/discountHistory')
|
||||||
|
this.fetching = false
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async index() {
|
async index() {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.fetching = true
|
this.fetching = true
|
||||||
@@ -18,12 +44,7 @@ export const useDiscountStore = defineStore('discounts', {
|
|||||||
return this.items = data
|
return this.items = data
|
||||||
|
|
||||||
},
|
},
|
||||||
async add(form) {
|
|
||||||
this.fetching = true
|
|
||||||
const { status, data } = await axios.post('/admins/discount', form)
|
|
||||||
this.fetching = false
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
async getDiscount(id) {
|
async getDiscount(id) {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.fetching = true
|
this.fetching = true
|
||||||
|
|||||||
+16
-6
@@ -8,18 +8,28 @@ export const useFaqStore = defineStore('faq', {
|
|||||||
total: 0
|
total: 0
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async index(page, pageSize) {
|
async index() {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.fetching = true
|
this.fetching = true
|
||||||
|
const { status, data } = await axios.get('/admins/faq/category')
|
||||||
const params = { page, pageSize }
|
|
||||||
const { status, data } = await axios.get('/admin/user/list', { params })
|
|
||||||
|
|
||||||
this.fetching = false
|
this.fetching = false
|
||||||
this.total = 30
|
|
||||||
this.items = data;
|
this.items = data;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
async remove(id) {
|
||||||
|
const data = await axios.delete(`/admins/faq/category/${id}`)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
async create(form) {
|
||||||
|
const data = await axios.post('/admins/faq/category', form)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async confirm(item, value) {
|
async confirm(item, value) {
|
||||||
item.confirming = true
|
item.confirming = true
|
||||||
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
|
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ export const useProductsStore = defineStore('products', {
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
},
|
||||||
|
async update(course) {
|
||||||
|
const { status, data } = await axios.put('/course', course)
|
||||||
|
return data
|
||||||
|
|
||||||
},
|
},
|
||||||
async delete(id) {
|
async delete(id) {
|
||||||
const { status, data } = await axios.delete(`/course/${id}`, id)
|
const { status, data } = await axios.delete(`/course/${id}`, id)
|
||||||
|
|||||||
+1
-38
@@ -8,43 +8,6 @@ export const useStaticsStore = defineStore('statics', {
|
|||||||
fetching: true,
|
fetching: true,
|
||||||
total: 0
|
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: {
|
actions: {
|
||||||
async index() {
|
async index() {
|
||||||
this.fetching = true
|
this.fetching = true
|
||||||
@@ -70,7 +33,7 @@ export const useStaticsStore = defineStore('statics', {
|
|||||||
name: id.value
|
name: id.value
|
||||||
})
|
})
|
||||||
|
|
||||||
//console.log('result', result);
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export const useTicketsStore = defineStore('tickets', {
|
|||||||
name: id.value
|
name: id.value
|
||||||
})
|
})
|
||||||
|
|
||||||
//console.log('result', result);
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
+7
-40
@@ -14,9 +14,7 @@ export const useUploaderStore = defineStore('uploader', {
|
|||||||
|
|
||||||
form.append('directoryName', 'directoryName')
|
form.append('directoryName', 'directoryName')
|
||||||
form.append('file', file)
|
form.append('file', file)
|
||||||
// for (var pair of form.entries()) {
|
|
||||||
// console.log('pairs :',pair[0]+ ', ' + pair[1]);
|
|
||||||
// }
|
|
||||||
let config = {
|
let config = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "multipart/form-data",
|
"Content-Type": "multipart/form-data",
|
||||||
@@ -28,49 +26,18 @@ export const useUploaderStore = defineStore('uploader', {
|
|||||||
form,
|
form,
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
console.log('result', result);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('err', error);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// const result = await axios.post('/uploader', form,
|
|
||||||
// {
|
|
||||||
// headers: { "Content-Type": "multipart/form-data" }
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
// console.log(result);
|
|
||||||
// return result
|
|
||||||
// const result = await axios({
|
|
||||||
// method: "post",
|
|
||||||
// url: "/uploader",
|
|
||||||
// data: form,
|
|
||||||
// headers: { "Content-Type": "multipart/form-data" },
|
|
||||||
// })
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// .then(function (response) {
|
|
||||||
|
|
||||||
// console.log(response);
|
|
||||||
// })
|
|
||||||
// .catch(function (response) {
|
|
||||||
|
|
||||||
// console.log(response);
|
|
||||||
// });
|
|
||||||
// console.log('result', result);
|
|
||||||
// return result;
|
|
||||||
},
|
},
|
||||||
|
async getUrl(fileName){
|
||||||
|
const data = await axios.get(`/uploader/file?fileName=${fileName}`)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
async bulk(files) {
|
async bulk(files) {
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ export const useUsersStore = defineStore('users', {
|
|||||||
this.items = data;
|
this.items = data;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
async changeStatus(form) {
|
||||||
|
const { status, data } = await axios.patch('/admin/user/status', form)
|
||||||
|
return data
|
||||||
|
},
|
||||||
async confirm(item, value) {
|
async confirm(item, value) {
|
||||||
item.confirming = true
|
item.confirming = true
|
||||||
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
|
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ export const treeTogglerRTL = (selector) => {
|
|||||||
element.style['margin-left'] = null
|
element.style['margin-left'] = null
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+27
-4
@@ -4,13 +4,13 @@
|
|||||||
<div class="grid grid-cols-2 gap-7">
|
<div class="grid grid-cols-2 gap-7">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="data.title" />
|
<InputText class="w-full" v-model="data.expertise" />
|
||||||
<label>{{ $t("حوزه های تخصصی") }}</label>
|
<label>{{ $t("تعداد حوزه های تخصصی") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="data.title" />
|
<InputText class="w-full" v-model="data.student" />
|
||||||
<label>{{ $t("تعداد دانشجو ها") }}</label>
|
<label>{{ $t("تعداد دانشجو ها") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<div class="grid grid-cols-2 gap-7 mt-7">
|
<div class="grid grid-cols-2 gap-7 mt-7">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText v-model="data.student" class="w-full" />
|
<InputText v-model="data.title" class="w-full" />
|
||||||
<label>{{ $t("تعداد عناوین آموزشی") }}</label>
|
<label>{{ $t("تعداد عناوین آموزشی") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,8 +62,31 @@
|
|||||||
title: 0,
|
title: 0,
|
||||||
hours: 0,
|
hours: 0,
|
||||||
});
|
});
|
||||||
|
const saving = ref(false);
|
||||||
const save = async (data) => {
|
const save = async (data) => {
|
||||||
|
saving.value = true;
|
||||||
const result = await store.edit(data);
|
const result = await store.edit(data);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
};
|
};
|
||||||
const expandedKeys = ref({});
|
const expandedKeys = ref({});
|
||||||
|
|
||||||
|
|||||||
+23
-8
@@ -9,7 +9,7 @@
|
|||||||
@click="create()"
|
@click="create()"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<DataTable :value="store.items">
|
<DataTable :value="videos">
|
||||||
|
|
||||||
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
|
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
|
||||||
<Column field="description" :header="$t('description')" />
|
<Column field="description" :header="$t('description')" />
|
||||||
@@ -73,9 +73,9 @@
|
|||||||
<template #empty>
|
<template #empty>
|
||||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<!-- <template #footer>
|
||||||
<Pagination :total="store.total" />
|
<Pagination :total="store.total" />
|
||||||
</template>
|
</template> -->
|
||||||
</DataTable>
|
</DataTable>
|
||||||
<!-- <Galleria
|
<!-- <Galleria
|
||||||
v-model:visible="galleryVes"
|
v-model:visible="galleryVes"
|
||||||
@@ -95,18 +95,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const { toast, dialog, confirm, t } = inject('service')
|
const { toast, dialog, confirm, t } = inject('service')
|
||||||
|
|
||||||
import { ref, watchEffect, reactive, defineAsyncComponent, inject } from "vue";
|
import { ref, watchEffect, reactive, defineAsyncComponent, inject, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { useHeadlinesStore } from "@/stores/Headlines"
|
import { useHeadlinesStore } from "@/stores/Headlines"
|
||||||
const store = useHeadlinesStore()
|
const store = useHeadlinesStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const videos = reactive({})
|
const videos = ref()
|
||||||
const HeadlineDetail = defineAsyncComponent(() => import('../components/HeadlineDetail.vue'));
|
const HeadlineDetail = defineAsyncComponent(() => import('../components/HeadlineDetail.vue'));
|
||||||
const AddVideo = defineAsyncComponent(() => import('../components/AddVideo.vue'));
|
const AddVideo = defineAsyncComponent(() => import('../components/AddVideo.vue'));
|
||||||
watchEffect(async() => {
|
const EditVideo = defineAsyncComponent(() => import('../components/EditVideo.vue'));
|
||||||
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) => {
|
const show = (product) => {
|
||||||
dialog.open(HeadlineDetail, {
|
dialog.open(HeadlineDetail, {
|
||||||
props: { modal: true, closable: true, header: t('پیش نمایش ویدیو') },
|
props: { modal: true, closable: true, header: t('پیش نمایش ویدیو') },
|
||||||
@@ -125,6 +129,17 @@ const create = () =>{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const edit = (product) => {
|
||||||
|
dialog.open(EditVideo, {
|
||||||
|
props: { modal: true, closable: true, header: t('ویرایش ویدیو') },
|
||||||
|
data: {
|
||||||
|
product
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const remove = async(data) => {
|
const remove = async(data) => {
|
||||||
confirm.require({
|
confirm.require({
|
||||||
message: t('Are you sure you want to proceed?'),
|
message: t('Are you sure you want to proceed?'),
|
||||||
|
|||||||
+8
-11
@@ -33,19 +33,13 @@
|
|||||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<!--
|
|
||||||
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<Pagination :total="store.total" />
|
|
||||||
</template>
|
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</Panel>
|
</Panel>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useUsersStore } from "@/stores/users";
|
import { useUsersStore } from "@/stores/users";
|
||||||
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue";
|
import { inject, reactive, defineAsyncComponent, ref, watchEffect, watch } from "vue";
|
||||||
const store = useUsersStore();
|
const store = useUsersStore();
|
||||||
const admins = ref()
|
const admins = ref()
|
||||||
|
|
||||||
@@ -61,9 +55,12 @@ const show = (user) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
await store.index(1, 100);
|
await store.index();
|
||||||
console.log("user", store.items);
|
admins.value = store.items.user.filter(item => item.roles[1] === 'Admin' );
|
||||||
admins.value = store.items.filter(item => item.roles[1] === 'Admin' );
|
|
||||||
console.log('filter',admins);
|
|
||||||
});
|
});
|
||||||
|
watch(admins, async()=>{
|
||||||
|
await store.index();
|
||||||
|
|
||||||
|
admins.value = store.items.user.filter(item => item.roles[1] === 'Admin' );
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<Panel :header="$t('پاسخ به نظرات')">
|
<Panel :header="$t('پاسخ به نظرات')">
|
||||||
<div v-for="item in data.comments" :key="item.id">
|
<div v-for="item in data.comments" :key="item.id">
|
||||||
|
|
||||||
<div class="card mb-8">
|
<div class="card mb-8">
|
||||||
<Panel toggleable>
|
<Panel toggleable>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Avatar :image="item.createdBy.avatarMedia" size="large" shape="circle" />
|
<Avatar v-if="item.createdBy.avatarMedia" :image="item.createdBy.avatarMedia" size="large" shape="circle" />
|
||||||
<span class="font-bold">{{item.createdBy.name}}</span>
|
<Avatar v-else image="/src/assets/alt.png" size="large" shape="circle" />
|
||||||
|
<span v-if="item.createdBy.name" class="font-bold">{{item.createdBy.name}}</span>
|
||||||
|
<span v-else class="font-bold">کاربر بدون نام</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -80,17 +83,17 @@ const reply = (id) =>{
|
|||||||
}
|
}
|
||||||
const sendReply = async() =>{
|
const sendReply = async() =>{
|
||||||
if (replyTarget.value === null || replyTarget.value === '') {
|
if (replyTarget.value === null || replyTarget.value === '') {
|
||||||
return console.log('کامنت مورد نظر را انتخاب کنید');
|
|
||||||
}
|
}
|
||||||
if (value.value === null || value.value === '') {
|
if (value.value === null || value.value === '') {
|
||||||
return console.log('متن پیام را وارد کنید');
|
|
||||||
}
|
}
|
||||||
// console.log(value.value);
|
|
||||||
const rep = await commentStore.reply(route.params.id, {
|
const rep = await commentStore.reply(route.params.id, {
|
||||||
text: value.value,
|
text: value.value,
|
||||||
parent: replyTarget.value
|
parent: replyTarget.value
|
||||||
})
|
})
|
||||||
console.log('rep', rep);
|
|
||||||
}
|
}
|
||||||
const items = ref([
|
const items = ref([
|
||||||
{
|
{
|
||||||
@@ -120,6 +123,6 @@ const save = () => {
|
|||||||
|
|
||||||
watchEffect(async() => {
|
watchEffect(async() => {
|
||||||
data.value = await store.edit(route.params.id)
|
data.value = await store.edit(route.params.id)
|
||||||
console.log('store', data.value.comments);
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
+80
-79
@@ -1,117 +1,118 @@
|
|||||||
<template>
|
<template>
|
||||||
<Panel :header="$t('categories')">
|
<Panel :header="$t('categories')">
|
||||||
<template #icons>
|
<template #icons>
|
||||||
<Button icon="pi pi-plus" :label="$t('add')" severity="success" @click="create()" />
|
<Button
|
||||||
|
icon="pi pi-plus"
|
||||||
|
:label="$t('add')"
|
||||||
|
severity="success"
|
||||||
|
@click="create()"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<TreeTable :value="store.items" v-model:expandedKeys="expandedKeys">
|
<DataTable :value="categories">
|
||||||
|
|
||||||
|
|
||||||
<Column field="name" :header="$t('name')" />
|
<Column field="name" :header="$t('name')" />
|
||||||
|
|
||||||
<!-- <Column :header="$t('image')">
|
|
||||||
<template #body="{ node: { data: { headerImage } } }">
|
|
||||||
<Image :src="headerImage" imageClass="rounded h-10" class="align-middle" preview
|
|
||||||
:pt="{ toolbar: 'flex-row-reverse' }" />
|
|
||||||
</template>
|
|
||||||
</Column> -->
|
|
||||||
<Column field="id" :header="$t('id')" />
|
<Column field="id" :header="$t('id')" />
|
||||||
|
<Column
|
||||||
|
:field="({ createdAt }) => jDate(createdAt)"
|
||||||
|
:header="$t('createdAt')"
|
||||||
|
/>
|
||||||
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
|
|
||||||
|
|
||||||
|
|
||||||
<Column headerClass="w-32" :header="$t('options')">
|
<Column headerClass="w-32" :header="$t('options')">
|
||||||
<template #body="{ node: { data } }">
|
<template #body="{ data }">
|
||||||
<div class="flex justify-start">
|
<div class="flex justify-start">
|
||||||
<!-- <Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(data)" /> -->
|
<!-- <Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(categories)" />
|
||||||
<Button icon="pi pi-trash" rounded text severity="danger" @click="remove(data)"
|
-->
|
||||||
:loading="data.removing" />
|
<Button
|
||||||
|
icon="pi pi-trash"
|
||||||
|
rounded
|
||||||
|
text
|
||||||
|
severity="danger"
|
||||||
|
@click="remove(data)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Column>
|
</Column>
|
||||||
|
</DataTable>
|
||||||
<template #empty>
|
|
||||||
<p>{{ store.fetching ? $t('loading') : $t('emptyTable') }}</p>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<Pagination :total="store.total" />
|
|
||||||
</template>
|
|
||||||
</TreeTable>
|
|
||||||
</Panel>
|
</Panel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { jDate } from '@/utils/jDate';
|
import { jDate } from "@/utils/jDate";
|
||||||
import { treeTogglerRTL } from '@/utils/tree-toggler-rtl';
|
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
|
||||||
import { inject, defineAsyncComponent, watchEffect, ref, watch, nextTick } from 'vue';
|
import {
|
||||||
import { useCategoriesStore } from '@/stores/categoreis';
|
inject,
|
||||||
import { useRoute } from 'vue-router';
|
defineAsyncComponent,
|
||||||
|
watchEffect,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
nextTick,
|
||||||
|
} from "vue";
|
||||||
|
import { useCategoriesStore } from "@/stores/categoreis";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
const categories = ref();
|
||||||
|
// const expandedKeys = ref({});
|
||||||
|
|
||||||
const expandedKeys = ref({});
|
const CategoryForm = defineAsyncComponent(() =>
|
||||||
|
import("../components/CategoryForm.vue")
|
||||||
|
);
|
||||||
|
|
||||||
const CategoryForm = defineAsyncComponent(() => import('../components/CategoryForm.vue'));
|
const { dialog, confirm, toast, t } = inject("service");
|
||||||
|
|
||||||
const { dialog, confirm, toast, t } = inject('service')
|
const store = useCategoriesStore();
|
||||||
|
|
||||||
const store = useCategoriesStore()
|
const create = async() => {
|
||||||
|
|
||||||
const create = () => {
|
|
||||||
dialog.open(CategoryForm, {
|
dialog.open(CategoryForm, {
|
||||||
props: { modal: true, closable: false, header: t('createNewCategory') },
|
props: { modal: true, closable: false, header: t("createNewCategory") },
|
||||||
data: { categories: 'store.items' }
|
data: { categories: "store.items" },
|
||||||
});
|
});
|
||||||
|
await store.index(1,100)
|
||||||
};
|
};
|
||||||
|
|
||||||
const edit = (category) => {
|
// const edit = (category) => {
|
||||||
dialog.open(CategoryForm, {
|
// dialog.open(CategoryForm, {
|
||||||
props: { modal: true, closable: false, header: t('editCategory') },
|
// props: { modal: true, closable: false, header: t('editCategory') },
|
||||||
data: { categories: store.items, category }
|
// data: { categories: store.items, category }
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
|
|
||||||
const remove = (category) => {
|
const remove = (data) => {
|
||||||
// console.log(category.id);
|
|
||||||
confirm.require({
|
confirm.require({
|
||||||
message: t('Are you sure you want to proceed?'),
|
message: t("Are you sure you want to proceed?"),
|
||||||
header: t('Danger Zone'),
|
header: t("Danger Zone"),
|
||||||
icon: 'pi pi-info-circle',
|
icon: "pi pi-info-circle",
|
||||||
acceptClass: 'p-button-danger p-button-sm',
|
acceptClass: "p-button-danger p-button-sm",
|
||||||
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
|
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
|
||||||
rejectLabel: t('cancel'),
|
rejectLabel: t("cancel"),
|
||||||
acceptLabel: t('beOmitted'),
|
acceptLabel: t("beOmitted"),
|
||||||
defaultFocus: 'reject',
|
defaultFocus: "reject",
|
||||||
accept: async () => {
|
accept: async () => {
|
||||||
category.removing = true
|
const deleted = await store.remove(data.id);
|
||||||
|
if (deleted.status === 200 || deleted.status === 201) {
|
||||||
const { status, data } = await store.remove(category.id);
|
|
||||||
|
|
||||||
category.removing = false
|
|
||||||
|
|
||||||
if (status === 200)
|
|
||||||
toast.add({
|
toast.add({
|
||||||
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("دسته بندی حذف شد"),
|
||||||
|
life: 3000,
|
||||||
});
|
});
|
||||||
else
|
|
||||||
|
} else
|
||||||
toast.add({
|
toast.add({
|
||||||
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("خطا، مجدد تلاش کنید"),
|
||||||
|
life: 3000,
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const route = useRoute()
|
watchEffect(async () => {
|
||||||
|
categories.value = await store.index(1, 100);
|
||||||
watchEffect(() => {
|
});
|
||||||
const { page = 1, rows = 10 } = route.query
|
watch(categories ,async () => {
|
||||||
store.index(page, rows)
|
categories.value = await store.index(1, 100);
|
||||||
})
|
});
|
||||||
watch(store , () =>{
|
|
||||||
store.index()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(expandedKeys, () => nextTick(() => treeTogglerRTL('button.p-treetable-toggler')))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
+5
-56
@@ -38,44 +38,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Column>
|
</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>
|
<template #empty>
|
||||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<!-- <template #footer>
|
||||||
<Pagination :total="store.total" />
|
<Pagination :total="store.total" />
|
||||||
</template>
|
</template> -->
|
||||||
</DataTable>
|
</DataTable>
|
||||||
<Galleria
|
<Galleria
|
||||||
v-model:visible="galleryVes"
|
v-model:visible="galleryVes"
|
||||||
@@ -131,7 +100,6 @@
|
|||||||
})
|
})
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const show = (data) => {
|
const show = (data) => {
|
||||||
console.log(data);
|
|
||||||
router.push(`/comments/${data._id}`)
|
router.push(`/comments/${data._id}`)
|
||||||
}
|
}
|
||||||
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
||||||
@@ -144,7 +112,7 @@ console.log(data);
|
|||||||
|
|
||||||
|
|
||||||
const remove = (data) => {
|
const remove = (data) => {
|
||||||
console.log('done', data._id)
|
|
||||||
confirm.require({
|
confirm.require({
|
||||||
message: t('Are you sure you want to proceed?'),
|
message: t('Are you sure you want to proceed?'),
|
||||||
header: t('Danger Zone'),
|
header: t('Danger Zone'),
|
||||||
@@ -156,25 +124,10 @@ console.log(data);
|
|||||||
defaultFocus: 'reject',
|
defaultFocus: 'reject',
|
||||||
accept: async () => {
|
accept: async () => {
|
||||||
const deleted = await store.delete(data._id)
|
const deleted = await store.delete(data._id)
|
||||||
console.log(deleted);
|
|
||||||
await store.index()
|
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
|
|
||||||
// });
|
|
||||||
|
|
||||||
// },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,11 +138,7 @@ console.log(data);
|
|||||||
watchEffect(async() => {
|
watchEffect(async() => {
|
||||||
const { page = 1, pageSize = 10 } = route.query
|
const { page = 1, pageSize = 10 } = route.query
|
||||||
await store.index(page, pageSize)
|
await store.index(page, pageSize)
|
||||||
console.log(store.items[2].image);
|
|
||||||
console.log(String.fromCharCode(store.items[2].image)).join('');
|
|
||||||
// store.items.forEach((product) => {
|
|
||||||
// product.status = { value: t('active'), severity: 'success' };
|
|
||||||
// })
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Panel :header="$t('coupons')">
|
<Panel :header="$t('coupons')">
|
||||||
<DataTable :value="store.items">
|
<DataTable :value="store.items.discount">
|
||||||
|
|
||||||
<Column field="code" :header="$t('کد تخفیف')" />
|
<Column field="code" :header="$t('کد تخفیف')" />
|
||||||
<Column field="id" :header="$t('id')" />
|
<Column field="id" :header="$t('id')" />
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
|
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
|
||||||
<Column :field="({ startDate }) => jDate(startDate)" :header="$t('تاریخ شروع')" />
|
<Column :field="({ startDate }) => jDate(startDate)" :header="$t('تاریخ شروع')" />
|
||||||
<Column :field="({ endDate }) => jDate(endDate)" :header="$t('تاریخ انقضا')" />
|
<Column :field="({ endDate }) => jDate(endDate)" :header="$t('تاریخ انقضا')" />
|
||||||
<Column field="status" :header="$t('status')" />
|
<Column field="statusText" :header="$t('status')" />
|
||||||
|
|
||||||
|
|
||||||
<Column headerClass="w-32">
|
<Column headerClass="w-32">
|
||||||
@@ -34,14 +34,11 @@
|
|||||||
<template #empty>
|
<template #empty>
|
||||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<!--
|
<!--
|
||||||
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
|
<template #footer>
|
||||||
-->
|
|
||||||
|
|
||||||
<Pagination :total="store.total" />
|
<Pagination :total="store.total" />
|
||||||
</template>
|
</template> -->
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</Panel>
|
</Panel>
|
||||||
</template>
|
</template>
|
||||||
@@ -68,11 +65,10 @@ const show = (coupon) => {
|
|||||||
}
|
}
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const edit = (data) =>{
|
const edit = (data) =>{
|
||||||
console.log(data.id);
|
|
||||||
router.push(`/coupon/${data.id}`)
|
router.push(`/coupon/${data.id}`)
|
||||||
}
|
}
|
||||||
watchEffect( async() =>{
|
watchEffect( async() =>{
|
||||||
await store.index()
|
await store.index()
|
||||||
console.log('data', data);
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
+8
-12
@@ -3,7 +3,7 @@
|
|||||||
<template #icons>
|
<template #icons>
|
||||||
<Button
|
<Button
|
||||||
icon="pi pi-plus"
|
icon="pi pi-plus"
|
||||||
:label="$t('add')"
|
:label="$t('ایجاد دوره ی جدید')"
|
||||||
severity="success"
|
severity="success"
|
||||||
@click="create()"
|
@click="create()"
|
||||||
/>
|
/>
|
||||||
@@ -97,9 +97,9 @@
|
|||||||
<template #empty>
|
<template #empty>
|
||||||
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<!-- <template #footer>
|
||||||
<Pagination :total="store.total" />
|
<Pagination :total="store.items.count" />
|
||||||
</template>
|
</template> -->
|
||||||
</DataTable>
|
</DataTable>
|
||||||
<Galleria
|
<Galleria
|
||||||
v-model:visible="galleryVes"
|
v-model:visible="galleryVes"
|
||||||
@@ -161,6 +161,7 @@ const show = (product) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
|
||||||
|
|
||||||
const store = useProductsStore()
|
const store = useProductsStore()
|
||||||
@@ -172,7 +173,7 @@ const edit = (data) =>{
|
|||||||
|
|
||||||
|
|
||||||
const remove = (data) => {
|
const remove = (data) => {
|
||||||
console.log('done', data._id)
|
|
||||||
confirm.require({
|
confirm.require({
|
||||||
message: t('Are you sure you want to proceed?'),
|
message: t('Are you sure you want to proceed?'),
|
||||||
header: t('Danger Zone'),
|
header: t('Danger Zone'),
|
||||||
@@ -184,7 +185,7 @@ const remove = (data) => {
|
|||||||
defaultFocus: 'reject',
|
defaultFocus: 'reject',
|
||||||
accept: async () => {
|
accept: async () => {
|
||||||
const deleted = await store.delete(data._id)
|
const deleted = await store.delete(data._id)
|
||||||
console.log(deleted);
|
|
||||||
await store.index()
|
await store.index()
|
||||||
}
|
}
|
||||||
// product.removing = true
|
// product.removing = true
|
||||||
@@ -206,18 +207,13 @@ const remove = (data) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const create = () => router.push('/create')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
watchEffect(async() => {
|
watchEffect(async() => {
|
||||||
const { page = 1, pageSize = 10 } = route.query
|
const { page = 1, pageSize = 10 } = route.query
|
||||||
await store.index(page, pageSize)
|
await store.index(page, pageSize)
|
||||||
console.log(store.items[2].image);
|
|
||||||
console.log(String.fromCharCode(store.items[2].image)).join('');
|
|
||||||
// store.items.forEach((product) => {
|
|
||||||
// product.status = { value: t('active'), severity: 'success' };
|
|
||||||
// })
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+161
-84
@@ -1,23 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="flex flex-col gap-4">
|
<section class="flex flex-col gap-4">
|
||||||
<Panel :header="$t('createCourse')">
|
<Panel :header="$t('createCourse')">
|
||||||
<div class="grid grid-cols-3 gap-7">
|
<div class="flex">
|
||||||
|
<div class="basis-4/6">
|
||||||
|
<div class="grid grid-cols-2 gap-7">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="newCourse.name" />
|
<InputText
|
||||||
|
@change="setSlug"
|
||||||
|
class="w-full"
|
||||||
|
v-model="newCourse.name"
|
||||||
|
/>
|
||||||
<label>{{ $t("title") }}</label>
|
<label>{{ $t("title") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="newCourse.language" />
|
<Dropdown
|
||||||
|
v-model="language"
|
||||||
|
@change="selectLanguage"
|
||||||
|
:options="['انگلیسی', 'فارسی']"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
|
||||||
<label>{{ $t("زبان آموزش") }}</label>
|
<label>{{ $t("زبان آموزش") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText v-model="newCourse.duration" class="w-full" />
|
<InputText
|
||||||
|
v-model="newCourse.duration"
|
||||||
|
class="w-full"
|
||||||
|
prefix="ساعت"
|
||||||
|
/>
|
||||||
<label>{{ $t("مدت زمان دوره") }}</label>
|
<label>{{ $t("مدت زمان دوره") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,27 +42,13 @@
|
|||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
v-model="level"
|
v-model="level"
|
||||||
|
@change="selectLevel"
|
||||||
:options="['مبتدی', 'متوسط', 'پیشرفته']"
|
:options="['مبتدی', 'متوسط', 'پیشرفته']"
|
||||||
|
|
||||||
|
|
||||||
class="w-full"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
<label>{{ $t("سطح دوره") }}</label>
|
<label>{{ $t("سطح دوره") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
|
||||||
<FloatLabel>
|
|
||||||
<InputText class="w-full" v-model="newCourse.teacher" />
|
|
||||||
<label>{{ $t("مدرس دوره") }}</label>
|
|
||||||
</FloatLabel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="flex flex-col gap-1">
|
|
||||||
<FloatLabel>
|
|
||||||
<InputText class="w-full" v-model="newCourse.categories" />
|
|
||||||
<label>{{ $t("دسته بندی") }}</label>
|
|
||||||
</FloatLabel>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
@@ -60,7 +62,7 @@
|
|||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="newCourse.videosCount" />
|
<InputText class="w-full" v-model="newCourse.countOfVideos" />
|
||||||
<label>{{ $t("تعداد ویدیو ها") }}</label>
|
<label>{{ $t("تعداد ویدیو ها") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -76,49 +78,64 @@
|
|||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<InputText class="pointer-events-none truncate" dir="ltr" />
|
<InputText class="pointer-events-none truncate" dir="ltr" />
|
||||||
<Button severity="secondary">
|
<Button severity="secondary" :loading="saving">
|
||||||
<i class="pi pi-image ml-2"></i>
|
<i class="pi pi-image ml-2"></i>
|
||||||
<span>{{ $t("chooseImage") }}</span>
|
<span>{{ $t("chooseImage") }}</span>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
class="opacity-0 absolute inset-0 cursor-pointer"
|
class="opacity-0 absolute inset-0 cursor-pointer"
|
||||||
|
|
||||||
@input="chooseImage"
|
@input="chooseImage"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
<label>{{ $t("image") }}</label>
|
<label>{{ $t("image") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full mt-7">
|
||||||
<div class="flex w-full mt-7">
|
|
||||||
<div class="basis-4/6">
|
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<Editor
|
<Editor
|
||||||
v-model="newCourse.description"
|
v-model="newCourse.description"
|
||||||
placeholder="توضیحات"
|
placeholder="توضیحات"
|
||||||
editorStyle="height: 320px"
|
editorStyle="height: 315px"
|
||||||
/>
|
/>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="basis-2/6 ms-8">
|
|
||||||
<div class="card flex flex-col gap-2 justify-center">
|
|
||||||
<Listbox
|
<Listbox
|
||||||
listStyle="height:240px"
|
listStyle="height:240px"
|
||||||
emptyMessage="دسته بندی وجود ندارد"
|
emptyMessage="دسته بندی وجود ندارد"
|
||||||
emptyFilterMessage="دسته بندی وجود ندارد"
|
emptyFilterMessage="دسته بندی وجود ندارد"
|
||||||
v-model="selectedCity"
|
v-model="selectedCategory"
|
||||||
|
@change="selectCategory"
|
||||||
filterPlaceholder=" دسته بندی"
|
filterPlaceholder=" دسته بندی"
|
||||||
:options="cities"
|
:options="cities"
|
||||||
filter
|
filter
|
||||||
optionLabel="name"
|
optionLabel="name"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div></div>
|
<Listbox
|
||||||
|
listStyle="height:240px"
|
||||||
|
emptyMessage="مدرس مورد نظر یافت نشد"
|
||||||
|
emptyFilterMessage="مدرس وجود ندارد"
|
||||||
|
v-model="selectedteacher"
|
||||||
|
@change="selectTeacher"
|
||||||
|
filterPlaceholder=" مدرس دوره"
|
||||||
|
:options="teachers"
|
||||||
|
filter
|
||||||
|
optionLabel="name"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -144,11 +161,19 @@
|
|||||||
:label="$t('create')"
|
:label="$t('create')"
|
||||||
icon="pi pi-save"
|
icon="pi pi-save"
|
||||||
severity="success"
|
severity="success"
|
||||||
:loading="saving"
|
:loading="creating"
|
||||||
@click="create(newCourse)"
|
@click="create(newCourse)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
|
<Message
|
||||||
|
v-if="saving"
|
||||||
|
class="absolute right-12 bottom-8"
|
||||||
|
icon="pi pi-spinner-dotted"
|
||||||
|
severity="info"
|
||||||
|
>درحال آپلود تصویر...</Message
|
||||||
|
>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -171,88 +196,142 @@ import { useStaticsStore } from "@/stores/statics";
|
|||||||
import { useUploaderStore } from "@/stores/uploader";
|
import { useUploaderStore } from "@/stores/uploader";
|
||||||
import { useProductsStore } from "@/stores/products";
|
import { useProductsStore } from "@/stores/products";
|
||||||
import { useCategoriesStore } from "@/stores/categoreis";
|
import { useCategoriesStore } from "@/stores/categoreis";
|
||||||
|
import { useUsersStore } from "../stores/users";
|
||||||
const categoriesStore = useCategoriesStore();
|
const categoriesStore = useCategoriesStore();
|
||||||
const courseStore = useProductsStore();
|
const courseStore = useProductsStore();
|
||||||
const uploadStore = useUploaderStore();
|
const uploadStore = useUploaderStore();
|
||||||
import { useRoute } from "vue-router";
|
const userStore = useUsersStore();
|
||||||
|
import { useRoute, useRouter } from "vue-router";
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const level = ref(null)
|
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 () => {
|
watchEffect(async () => {
|
||||||
const { page = 1, rows = 10 } = route.query;
|
const { page = 1, rows = 10 } = route.query;
|
||||||
const categoriess = await categoriesStore.index(page, rows);
|
const categoriess = await categoriesStore.index(page, rows);
|
||||||
console.log("cat", categoriess);
|
|
||||||
cities.value = categoriess;
|
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 selectedCity = ref();
|
||||||
const cities = ref();
|
const cities = ref();
|
||||||
|
const teachers = ref();
|
||||||
const levels = reactive(["آسان", "متوسط", "سخت"]);
|
const levels = reactive(["آسان", "متوسط", "سخت"]);
|
||||||
const newCourse = reactive({
|
const newCourse = reactive({
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
slug: "",
|
slug: "تست",
|
||||||
duration: "",
|
duration: "",
|
||||||
image: "",
|
image: "",
|
||||||
categories: "",
|
categories: "",
|
||||||
price: 0,
|
price: null,
|
||||||
teacher: "",
|
teacher: "",
|
||||||
completionPercentage: 0,
|
completionPercentage: null,
|
||||||
level: "",
|
level: "",
|
||||||
language: "",
|
language: "",
|
||||||
countOfVideos: 0,
|
countOfVideos: null,
|
||||||
totalFileSize: "",
|
totalFileSize: "",
|
||||||
});
|
});
|
||||||
const chooseImage = async (val) => {
|
const setSlug = () => {
|
||||||
console.log(val.target.files[0]);
|
newCourse.slug = newCourse.name;
|
||||||
const uploader = await uploadStore.uploader(val.target.files[0]);
|
|
||||||
console.log('uploader', uploader.data.url);
|
|
||||||
newCourse.image = uploader.data.url;
|
|
||||||
console.log("تصویر آپلود شد");
|
|
||||||
console.log("upldr", uploader);
|
|
||||||
};
|
};
|
||||||
const create = async (data) => {
|
const chooseImage = async (val) => {
|
||||||
if (newCourse.image === "") {
|
saving.value = true;
|
||||||
return console.log("تصویر دوره وارد نشده است");
|
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 (
|
if (
|
||||||
newCourse.name === "" ||
|
newCourse.name === "" ||
|
||||||
newCourse.description === "" ||
|
newCourse.description === "" ||
|
||||||
// newCourse.slug === "" ||
|
|
||||||
newCourse.duration === "" ||
|
newCourse.duration === "" ||
|
||||||
// newCourse.categories === "" ||
|
newCourse.categories === "" ||
|
||||||
// newCourse.teacher === "" ||
|
newCourse.price === null ||
|
||||||
newCourse.completionPercentage === 0 ||
|
newCourse.teacher === "" ||
|
||||||
// newCourse.level === "" ||
|
newCourse.completionPercentage === null ||
|
||||||
// newCourse.language === "" ||
|
newCourse.level === "" ||
|
||||||
newCourse.countOfVideos === "" ||
|
newCourse.language === "" ||
|
||||||
|
newCourse.countOfVideos === null ||
|
||||||
newCourse.totalFileSize === ""
|
newCourse.totalFileSize === ""
|
||||||
) {
|
) {
|
||||||
return console.log("تمام مقادیر را وارد کنید");
|
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("تصویر دوره را وارد کنید"),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
newCourse.name = data.name;
|
|
||||||
newCourse.description = data.description;
|
|
||||||
newCourse.slug = data.name;
|
|
||||||
newCourse.duration = data.duration;
|
|
||||||
// newCourse.categories = data.categories;
|
|
||||||
newCourse.categories = "66587ae698d0a0db14693a5e";
|
|
||||||
// newCourse.teacher = data.teacher;
|
|
||||||
newCourse.teacher = "66530b0480ef484e2bd19220";
|
|
||||||
// newCourse.completionPercentage = data.completionPercentage;
|
|
||||||
newCourse.completionPercentage = 20;
|
|
||||||
// newCourse.level = data.level;
|
|
||||||
newCourse.level = "متوسط";
|
|
||||||
// newCourse.language = data.language;
|
|
||||||
newCourse.language = "فارسی";
|
|
||||||
newCourse.countOfVideos = data.countOfVideos;
|
|
||||||
newCourse.totalFileSize = data.totalFileSize;
|
|
||||||
// console.log("newCourse: ", newCourse);
|
|
||||||
const creat = await courseStore.create(newCourse);
|
const creat = await courseStore.create(newCourse);
|
||||||
console.log("created", creat);
|
|
||||||
|
|
||||||
|
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("مشکل در ایجاد دوره، مجدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
// console.log("form", data);
|
|
||||||
// console.log("json", JSON.stringify(data));
|
|
||||||
// const { result } = await store.edit(data);
|
|
||||||
// console.log("res", result);
|
|
||||||
// };
|
|
||||||
const expandedKeys = ref({});
|
const expandedKeys = ref({});
|
||||||
|
|
||||||
const CategoryForm = defineAsyncComponent(() =>
|
const CategoryForm = defineAsyncComponent(() =>
|
||||||
@@ -265,9 +344,7 @@ const store = useStaticsStore();
|
|||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
await store.index();
|
await store.index();
|
||||||
// console.log(store.items);
|
|
||||||
|
|
||||||
//data.videosCount = store.items.hours;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -300,7 +377,7 @@ const uploader = useUploaderStore();
|
|||||||
const save = async () => {
|
const save = async () => {
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
|
|
||||||
console.log(form);
|
|
||||||
|
|
||||||
//Upload File
|
//Upload File
|
||||||
if (typeof form.coverImage === "object") {
|
if (typeof form.coverImage === "object") {
|
||||||
|
|||||||
+41
-14
@@ -55,14 +55,14 @@
|
|||||||
<label>{{ $t("نوع تخفیف") }}</label>
|
<label>{{ $t("نوع تخفیف") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="noe === 'مبلغ'" class="flex flex-col gap-1">
|
<div v-if="type === 'مبلغ'" class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="coupon.price" />
|
<InputText class="w-full" v-model="coupon.price" />
|
||||||
<label>{{ $t("مبلغ تخفیف") }}</label>
|
<label>{{ $t("مبلغ تخفیف") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="noe === 'درصد'" class="flex flex-col gap-1">
|
<div v-if="type === 'درصد'" class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="coupon.percent" />
|
<InputText class="w-full" v-model="coupon.percent" />
|
||||||
<label>{{ $t("درصد تخفیف") }}</label>
|
<label>{{ $t("درصد تخفیف") }}</label>
|
||||||
@@ -92,23 +92,29 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useProductsStore } from "@/stores/products";
|
import { useProductsStore } from "@/stores/products";
|
||||||
import { useDiscountStore } from "@/stores/discounts"
|
import { useDiscountStore } from "@/stores/discounts";
|
||||||
import { ref, watchEffect, reactive } from "vue";
|
import { ref, watchEffect, reactive, inject } from "vue";
|
||||||
import moment from 'moment-jalaali'
|
import moment from "moment-jalaali";
|
||||||
const date= ref(null)
|
import { useRouter } from "vue-router";
|
||||||
const date2= ref(null)
|
const { dialog, confirm, toast, t } = inject("service");
|
||||||
const noe = ref('مبلغ')
|
const date = ref(null);
|
||||||
|
const date2 = ref(null);
|
||||||
|
const noe = ref("مبلغ");
|
||||||
const store = useProductsStore();
|
const store = useProductsStore();
|
||||||
const discount = useDiscountStore();
|
const discount = useDiscountStore();
|
||||||
const data = ref(null);
|
const data = ref(null);
|
||||||
const type = ref(null);
|
const type = ref(null);
|
||||||
|
const router = useRouter()
|
||||||
const setType = (val) => {
|
const setType = (val) => {
|
||||||
if (val.value === 'درصد') {
|
if (val.value === "درصد") {
|
||||||
coupon.discountType = 'percent'
|
coupon.discountType = "percent";
|
||||||
} else {
|
} else {
|
||||||
coupon.discountType = 'price'
|
coupon.discountType = "price";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//const { dialog, confirm, toast, t } = inject("service");
|
||||||
|
|
||||||
const coupon = reactive({
|
const coupon = reactive({
|
||||||
startDate: "",
|
startDate: "",
|
||||||
endDate: "",
|
endDate: "",
|
||||||
@@ -116,11 +122,32 @@ const coupon = reactive({
|
|||||||
discountType: type.value,
|
discountType: type.value,
|
||||||
price: "",
|
price: "",
|
||||||
percent: null,
|
percent: null,
|
||||||
allUsers: true
|
allUsers: true,
|
||||||
});
|
});
|
||||||
|
const saving = ref(false);
|
||||||
const create = async (coupon) => {
|
const create = async (coupon) => {
|
||||||
const datas = await discount.add(coupon)
|
saving.value = true
|
||||||
|
const datas = await discount.add(coupon);
|
||||||
|
saving.value = false;
|
||||||
|
if (datas === true) {
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("کد تخفیف ایجاد شد"),
|
||||||
|
});
|
||||||
|
router.push('/Coupons')
|
||||||
|
} else{
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: "خطا رخ داد، مجدد تلاش کنید",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
+189
-48
@@ -1,23 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="flex flex-col gap-4">
|
<section class="flex flex-col gap-4">
|
||||||
<Panel :header="$t('ویرایش دوره')">
|
<Panel :header="$t('ویرایش اطلاعات دوره')">
|
||||||
<div class="grid grid-cols-3 gap-7">
|
<div class="flex">
|
||||||
|
<div class="basis-4/6">
|
||||||
|
<div class="grid grid-cols-2 gap-7">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="course.name" />
|
<InputText
|
||||||
|
@change="setSlug"
|
||||||
|
class="w-full"
|
||||||
|
v-model="course.name"
|
||||||
|
/>
|
||||||
<label>{{ $t("title") }}</label>
|
<label>{{ $t("title") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="course.language" />
|
<Dropdown
|
||||||
|
v-model="course.language"
|
||||||
|
@change="selectLanguage"
|
||||||
|
:options="['انگلیسی', 'فارسی']"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
|
||||||
<label>{{ $t("زبان آموزش") }}</label>
|
<label>{{ $t("زبان آموزش") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="course.duration" />
|
<InputText
|
||||||
|
v-model="course.duration"
|
||||||
|
class="w-full"
|
||||||
|
prefix="ساعت"
|
||||||
|
/>
|
||||||
<label>{{ $t("مدت زمان دوره") }}</label>
|
<label>{{ $t("مدت زمان دوره") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,32 +41,28 @@
|
|||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
:options="levels"
|
|
||||||
:optionLabel="levels"
|
|
||||||
:optionValue="levels"
|
|
||||||
v-model="course.level"
|
v-model="course.level"
|
||||||
|
@change="selectLevel"
|
||||||
|
:options="['مبتدی', 'متوسط', 'پیشرفته']"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
<label>{{ $t("سطح دوره") }}</label>
|
<label>{{ $t("سطح دوره") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
|
||||||
<FloatLabel>
|
|
||||||
<InputText class="w-full" v-model="course.teacher.name" />
|
|
||||||
<label>{{ $t("مدرس دوره") }}</label>
|
|
||||||
</FloatLabel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="course.completionPercentage" />
|
<InputText
|
||||||
|
class="w-full"
|
||||||
|
v-model="course.completionPercentage"
|
||||||
|
/>
|
||||||
<label>{{ $t("درصد تکمیل دوره") }}</label>
|
<label>{{ $t("درصد تکمیل دوره") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="course.videosCount" />
|
<InputText class="w-full" v-model="course.countOfVideos" />
|
||||||
<label>{{ $t("تعداد ویدیو ها") }}</label>
|
<label>{{ $t("تعداد ویدیو ها") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,20 +74,18 @@
|
|||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<InputText class="pointer-events-none truncate" dir="ltr" />
|
<InputText class="pointer-events-none truncate" dir="ltr" />
|
||||||
<Button severity="secondary">
|
<Button severity="secondary" :loading="saving">
|
||||||
<i class="pi pi-image ml-2"></i>
|
<i class="pi pi-image ml-2"></i>
|
||||||
<span>{{ $t("chooseImage") }}</span>
|
<span>{{ $t("chooseImage") }}</span>
|
||||||
<input
|
<input
|
||||||
:value="course.image"
|
|
||||||
type="file"
|
type="file"
|
||||||
|
:value="course.image"
|
||||||
class="opacity-0 absolute inset-0 cursor-pointer"
|
class="opacity-0 absolute inset-0 cursor-pointer"
|
||||||
accept="image/*"
|
@input="chooseImage"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
@@ -83,77 +93,153 @@
|
|||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full mt-7">
|
||||||
<div class="flex w-full mt-7">
|
|
||||||
<div class="basis-4/6">
|
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<Editor
|
<Editor
|
||||||
v-model="course.description"
|
v-model="course.description"
|
||||||
placeholder="توضیحات"
|
placeholder="توضیحات"
|
||||||
editorStyle="height: 320px"
|
editorStyle="height: 315px"
|
||||||
/>
|
/>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="basis-2/6 ms-8">
|
<div class="basis-2/6 flex flex-col gap-7 ms-7">
|
||||||
<div class="card flex flex-col gap-2 justify-center ">
|
<div class="flex flex-col gap-1">
|
||||||
|
<FloatLabel>
|
||||||
|
<InputText class="w-full" v-model="course.price" />
|
||||||
|
<label>{{ $t("قیمت دوره") }}</label>
|
||||||
|
</FloatLabel>
|
||||||
|
</div>
|
||||||
|
<span class="hidden">{{ course.price }}</span>
|
||||||
<Listbox
|
<Listbox
|
||||||
listStyle="height:240px"
|
listStyle="height:240px"
|
||||||
emptyMessage="دسته بندی وجود ندارد"
|
emptyMessage="دسته بندی وجود ندارد"
|
||||||
emptyFilterMessage="دسته بندی وجود ندارد"
|
emptyFilterMessage="دسته بندی وجود ندارد"
|
||||||
|
v-model="selectedCategory"
|
||||||
|
@change="selectCategory"
|
||||||
filterPlaceholder=" دسته بندی"
|
filterPlaceholder=" دسته بندی"
|
||||||
:options="cities"
|
:options="cities"
|
||||||
filter
|
filter
|
||||||
optionLabel="name"
|
optionLabel="name"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div></div>
|
<Listbox
|
||||||
|
listStyle="height:240px"
|
||||||
|
emptyMessage="مدرس مورد نظر یافت نشد"
|
||||||
|
emptyFilterMessage="مدرس وجود ندارد"
|
||||||
|
v-model="selectedteacher"
|
||||||
|
@change="selectTeacher"
|
||||||
|
filterPlaceholder=" مدرس دوره"
|
||||||
|
:options="teachers"
|
||||||
|
filter
|
||||||
|
optionLabel="name"
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="flex pt-4 justify-end">
|
||||||
<Button
|
<Button
|
||||||
:label="$t('edit')"
|
:label="$t('edit')"
|
||||||
icon="pi pi-save"
|
icon="pi pi-save"
|
||||||
severity="success"
|
severity="success"
|
||||||
:loading="saving"
|
:loading="creating"
|
||||||
@click="create(course)"
|
@click="editCourse"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
|
<Message
|
||||||
|
v-if="saving"
|
||||||
|
class="absolute right-12 bottom-8"
|
||||||
|
icon="pi pi-spinner-dotted"
|
||||||
|
severity="info"
|
||||||
|
>درحال آپلود تصویر...</Message
|
||||||
|
>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useProductsStore } from "@/stores/products";
|
import { useProductsStore } from "@/stores/products";
|
||||||
import { ref, watchEffect, reactive } from "vue";
|
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();
|
||||||
|
const courseStore = useProductsStore();
|
||||||
|
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);
|
||||||
const store = useProductsStore();
|
const store = useProductsStore();
|
||||||
const data = ref(null);
|
const data = ref(null);
|
||||||
|
const cities = ref();
|
||||||
|
|
||||||
|
const selectCategory = () => {
|
||||||
|
course.categories = selectedCategory.value.id;
|
||||||
|
};
|
||||||
|
const selectTeacher = () => {
|
||||||
|
course.teacher = selectedteacher.value.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const course = reactive({
|
const course = reactive({
|
||||||
id: "",
|
id: route.params.id,
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
slug: "",
|
slug: "",
|
||||||
duration: "",
|
duration: "",
|
||||||
image: "",
|
image: "",
|
||||||
categories: "",
|
categories: "",
|
||||||
price: 0,
|
price: "",
|
||||||
teacher: "",
|
teacher: "",
|
||||||
completionPercentage: 0,
|
completionPercentage: null,
|
||||||
level: "",
|
level: "",
|
||||||
language: "",
|
language: "",
|
||||||
countOfVideos: 0,
|
countOfVideos: null,
|
||||||
totalFileSize: "",
|
totalFileSize: "",
|
||||||
});
|
});
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
data.value = await store.edit(route.params.id);
|
data.value = await store.edit(route.params.id);
|
||||||
|
|
||||||
course.id = data.value.course.id;
|
|
||||||
course.name = data.value.course.name;
|
course.name = data.value.course.name;
|
||||||
course.description = data.value.course.description;
|
course.description = data.value.course.description;
|
||||||
course.slug = data.value.course.slug;
|
course.slug = data.value.course.slug;
|
||||||
@@ -171,13 +257,68 @@ watchEffect(async () => {
|
|||||||
|
|
||||||
// const levels = reactive(["آسان", "متوسط", "سخت"]);
|
// const levels = reactive(["آسان", "متوسط", "سخت"]);
|
||||||
|
|
||||||
const chooseImage = async (el) => {
|
const chooseImage = async (val) => {
|
||||||
// const uploader = await uploadStore.uploader(el.target.value);
|
saving.value = true;
|
||||||
// //console.log('uploader', uploader.data.url);
|
const uploader = await uploadStore.uploader(val.target.files[0]);
|
||||||
// newCourse.image = uploader.data.url;
|
course.image = uploader.data.url;
|
||||||
// console.log("تصویر آپلود شد");
|
saving.value = false;
|
||||||
// console.log('upldr',uploader);
|
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 { dialog, confirm, toast, t } = inject("service");
|
||||||
|
const router = useRouter()
|
||||||
|
const editCourse = async (data) => {
|
||||||
|
creating.value = true;
|
||||||
|
if (
|
||||||
|
course.name === "" ||
|
||||||
|
course.description === "" ||
|
||||||
|
course.duration === "" ||
|
||||||
|
course.categories === "" ||
|
||||||
|
course.price === null ||
|
||||||
|
course.teacher === "" ||
|
||||||
|
course.completionPercentage === null ||
|
||||||
|
course.level === "" ||
|
||||||
|
course.language === "" ||
|
||||||
|
course.countOfVideos === null ||
|
||||||
|
course.totalFileSize === ""
|
||||||
|
) {
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("تمام فیلد ها را وارد کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (course.image === "" || course.image === null) {
|
||||||
|
creating.value = false;
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("تصویر دوره را وارد کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const update = await courseStore.update(course);
|
||||||
|
creating.value = false
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("دوره با موفقیت ویرایش شد"),
|
||||||
|
});
|
||||||
|
router.push('/courses')
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data) => {};
|
|
||||||
</script>
|
</script>
|
||||||
@@ -85,14 +85,15 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useDiscountStore } from "@/stores/discounts";
|
import { useDiscountStore } from "@/stores/discounts";
|
||||||
import { ref, watchEffect, reactive } from "vue";
|
import { ref, watchEffect, reactive, inject } from "vue";
|
||||||
import moment from "moment-jalaali";
|
import moment from "moment-jalaali";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
const date = ref(null);
|
const date = ref(null);
|
||||||
const date2 = ref(null);
|
const date2 = ref(null);
|
||||||
const noe = ref("مبلغ");
|
const noe = ref("مبلغ");
|
||||||
const store = useDiscountStore();
|
const store = useDiscountStore();
|
||||||
const data = ref();
|
const data = ref();
|
||||||
|
const router = useRouter()
|
||||||
const setType = (val) => {
|
const setType = (val) => {
|
||||||
if (val.value === 'درصد') {
|
if (val.value === 'درصد') {
|
||||||
discount.discountType = 'percent'
|
discount.discountType = 'percent'
|
||||||
@@ -110,13 +111,35 @@ const discount = reactive({
|
|||||||
percent: 0,
|
percent: 0,
|
||||||
allUsers: true,
|
allUsers: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { dialog, confirm, toast, t } = inject("service");
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const saving = ref(false);
|
||||||
const create = async(val) => {
|
const create = async(val) => {
|
||||||
|
saving.value = true;
|
||||||
const posts = await store.update(val)
|
const posts = await store.update(val)
|
||||||
|
saving.value = false;
|
||||||
|
if (posts === true) {
|
||||||
|
toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("تخفیف ویرایش شد"),
|
||||||
|
});
|
||||||
|
router.push('/Coupons')
|
||||||
|
}else{
|
||||||
|
return toast.add({
|
||||||
|
life: 2000,
|
||||||
|
severity: "error",
|
||||||
|
summary: t("error"),
|
||||||
|
detail: t("خطا، محدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
data.value = await store.getDiscount(route.params.id);
|
data.value = await store.getDiscount(route.params.id);
|
||||||
console.log("this", data.value);
|
|
||||||
discount.id = data.value.id;
|
discount.id = data.value.id;
|
||||||
(discount.startDate = data.value.startDate),
|
(discount.startDate = data.value.startDate),
|
||||||
(discount.endDate = data.value.endDate),
|
(discount.endDate = data.value.endDate),
|
||||||
|
|||||||
+83
-2
@@ -1,8 +1,89 @@
|
|||||||
<template>
|
<template>
|
||||||
<h1>س</h1>
|
<Panel :header="$t('سوالات متداول')">
|
||||||
|
<template #icons>
|
||||||
|
<Button
|
||||||
|
icon="pi pi-plus"
|
||||||
|
:label="$t('ایجاد آیتم جدید')"
|
||||||
|
severity="success"
|
||||||
|
@click="create()"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<DataTable :value="store.items">
|
||||||
|
|
||||||
|
<Column field="title" :header="$t('title')" />
|
||||||
|
|
||||||
|
<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
|
||||||
|
@click="remove(data)"
|
||||||
|
icon="pi pi-trash"
|
||||||
|
rounded
|
||||||
|
text
|
||||||
|
severity="danger"
|
||||||
|
:loading="removing"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Column>
|
||||||
|
|
||||||
|
<template #empty>
|
||||||
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
|
</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>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineAsyncComponent, inject, ref, watchEffect } from "vue";
|
||||||
|
import { useFaqStore } from "@/stores/faq";
|
||||||
|
const store = useFaqStore();
|
||||||
|
const { toast, dialog, confirm, t } = inject('service');
|
||||||
|
const FaqDetails = defineAsyncComponent(() => import('../components/FaqDetails.vue'));
|
||||||
|
const AddFaq = defineAsyncComponent(() => import('../components/AddFaq.vue'));
|
||||||
|
const removing = ref(false)
|
||||||
|
const show = (product) => {
|
||||||
|
dialog.open(FaqDetails, {
|
||||||
|
props: { modal: true, closable: true, header: t('جزئیات آیتم') },
|
||||||
|
data: {
|
||||||
|
product
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const create = () =>{
|
||||||
|
dialog.open(AddFaq, {
|
||||||
|
props: { modal: true, closable: true, header: t('ایجاد آیتم جدید') },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const remove = async (item) => {
|
||||||
|
removing.value = true
|
||||||
|
const {data} = await store.remove(item.id)
|
||||||
|
removing.value = false
|
||||||
|
await store.index()
|
||||||
|
};
|
||||||
|
watchEffect(async() =>{
|
||||||
|
await store.index()
|
||||||
|
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
+41
-19
@@ -4,13 +4,13 @@
|
|||||||
<div class="grid grid-cols-1 gap-7">
|
<div class="grid grid-cols-1 gap-7">
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="data.title" />
|
<InputText class="w-full" v-model="data.header" />
|
||||||
<label>{{ $t("متن هدر") }}</label>
|
<label>{{ $t("متن هدر") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<Textarea v-model="data.description" rows="5" class="w-full" />
|
<Textarea v-model="data.titleText" rows="5" class="w-full" />
|
||||||
<label>{{ $t("توضیحات هدر") }}</label>
|
<label>{{ $t("توضیحات هدر") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText class="w-full" v-model="data.rates" />
|
<InputText class="w-full" v-model="data.rate" />
|
||||||
<label>{{ $t("امتیاز دانشجو ها") }}</label>
|
<label>{{ $t("امتیاز دانشجو ها") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,24 +79,45 @@ import {
|
|||||||
} from "vue";
|
} from "vue";
|
||||||
import { useStaticsStore } from "@/stores/statics";
|
import { useStaticsStore } from "@/stores/statics";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
student: 0,
|
header: "",
|
||||||
title: 0,
|
titleText: "",
|
||||||
expertise: 0,
|
student: null,
|
||||||
about: "string",
|
title: null,
|
||||||
|
rate: null,
|
||||||
|
expertise: null,
|
||||||
|
about: "",
|
||||||
});
|
});
|
||||||
const save = async (data) => {
|
const saving = ref(false);
|
||||||
// console.log('form', data);
|
async function save(data) {
|
||||||
// console.log('json', JSON.stringify(data));
|
saving.value = true;
|
||||||
const result = await store.edit(data);
|
const result = await store.edit(data);
|
||||||
// console.log("res", result);
|
|
||||||
};
|
|
||||||
const expandedKeys = ref({});
|
|
||||||
|
|
||||||
const CategoryForm = defineAsyncComponent(() =>
|
if (result.data === true) {
|
||||||
import("../components/CategoryForm.vue")
|
toast.add({
|
||||||
);
|
life: 2000,
|
||||||
|
severity: "success",
|
||||||
|
summary: t("successful"),
|
||||||
|
detail: t("اطلاعات با موفقیت ویرایش شد"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.add({
|
||||||
|
severity: "error",
|
||||||
|
life: 2000,
|
||||||
|
detail: t("خطا، مجدد تلاش کنید"),
|
||||||
|
summary: t("خطا، مجدد تلاش کنید"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const expandedKeys = ref({});
|
||||||
|
|
||||||
|
// const CategoryForm = defineAsyncComponent(() =>
|
||||||
|
// import("../components/CategoryForm.vue")
|
||||||
|
// );
|
||||||
|
|
||||||
const { dialog, confirm, toast, t } = inject("service");
|
const { dialog, confirm, toast, t } = inject("service");
|
||||||
|
|
||||||
@@ -104,11 +125,12 @@ const store = useStaticsStore();
|
|||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
await store.index();
|
await store.index();
|
||||||
// console.log(store.items);
|
data.header = store.items.header;
|
||||||
data.student = store.items.student;
|
data.student = store.items.student;
|
||||||
data.title = store.items.title;
|
data.title = store.items.title;
|
||||||
data.expertise = store.items.expertise;
|
data.expertise = store.items.expertise;
|
||||||
data.about = store.items.about;
|
data.about = store.items.about;
|
||||||
//data.videosCount = store.items.hours;
|
data.titleText = store.items.titleText;
|
||||||
|
data.rate = store.items.rate;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+66
-37
@@ -2,83 +2,112 @@
|
|||||||
<Card
|
<Card
|
||||||
v-if="!changeForm"
|
v-if="!changeForm"
|
||||||
class="m-auto w-96"
|
class="m-auto w-96"
|
||||||
:pt="{ title: 'text-center pt-4', content: 'flex flex-col gap-9 pt-8 h-30' }"
|
:pt="{
|
||||||
|
title: 'text-center pt-4',
|
||||||
|
content: 'flex flex-col gap-9 pt-8 h-30',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template #title> {{ $t('login') }} </template>
|
<template #title> {{ $t("login") }} </template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText v-model="number" class="w-full ltr" />
|
<InputText v-model="number" class="w-full ltr" />
|
||||||
<label>{{ $t('phoneNumber') }}</label>
|
<label>{{ $t("phoneNumber") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
<small v-if="errors.length > 0">{{ errors }}</small>
|
<small v-if="errors.length > 0">{{ errors }}</small>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<Button :label="$t('sendOtp')" class="w-full" :loading="loading" @click="login()" />
|
<Button
|
||||||
|
:label="$t('sendOtp')"
|
||||||
|
class="w-full"
|
||||||
|
:loading="loading"
|
||||||
|
@click="login()"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
v-else
|
v-else
|
||||||
class="m-auto w-96"
|
class="m-auto w-96"
|
||||||
:pt="{ title: 'text-center pt-4', content: 'flex flex-col gap-9 pt-8 h-30' }"
|
:pt="{
|
||||||
|
title: 'text-center pt-4',
|
||||||
|
content: 'flex flex-col gap-9 pt-8 h-30',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template #title> {{ $t('otp') }} </template>
|
<template #title> {{ $t("otp") }} </template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<FloatLabel>
|
<FloatLabel>
|
||||||
<InputText v-model="code" class="w-full ltr" />
|
<InputText v-model="code" class="w-full ltr" />
|
||||||
<label>{{ $t('otp') }}</label>
|
<label>{{ $t("otp") }}</label>
|
||||||
</FloatLabel>
|
</FloatLabel>
|
||||||
<small v-if="errors.length > 0">{{ errors }}</small>
|
<small v-if="errors.length > 0">{{ errors }}</small>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<Button :label="$t('login')" class="w-full" :loading="loading" @click="checkOtp()" />
|
<Button
|
||||||
|
:label="$t('login')"
|
||||||
|
class="w-full"
|
||||||
|
:loading="loading"
|
||||||
|
@click="checkOtp()"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { inject, reactive, ref } from 'vue'
|
import { inject, reactive, ref } from "vue";
|
||||||
import { useAdminsStore } from '@/stores/admins'
|
import { useAdminsStore } from "@/stores/admins";
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from "vue-router";
|
||||||
const { toast, t } = inject('service')
|
const { toast, t } = inject("service");
|
||||||
const loading = ref(false)
|
const loading = ref(false);
|
||||||
const changeForm = ref(false)
|
const changeForm = ref(false);
|
||||||
const store = useAdminsStore()
|
const store = useAdminsStore();
|
||||||
const errors = ref({})
|
const errors = ref({});
|
||||||
const number = ref(null)
|
const number = ref(null);
|
||||||
const code = ref(1234)
|
const code = ref(1234);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const login = async () => {
|
const login = async () => {
|
||||||
loading.value = true
|
loading.value = true;
|
||||||
const { status, data } = await store.login(number)
|
const { status, data } = await store.login(number);
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
changeForm.value = true
|
changeForm.value = true;
|
||||||
toast.add({
|
toast.add({
|
||||||
severity: 'success',
|
severity: "success",
|
||||||
life: 2000,
|
life: 2000,
|
||||||
detail: t('sendOtpSuccessfully'),
|
detail: t("sendOtpSuccessfully"),
|
||||||
summary: t('successful'),
|
summary: t("successful"),
|
||||||
closable: false
|
closable: false,
|
||||||
})
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
const checkOtp = async () => {
|
const checkOtp = async () => {
|
||||||
loading.value = true
|
loading.value = true;
|
||||||
const { status, data } = await store.sendOtp(number, code)
|
const { status, data } = await store.sendOtp(number, code);
|
||||||
console.log('com data', data)
|
console.log("login", data);
|
||||||
console.log('com status', status)
|
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
|
if (data.roles[0] === "Admin") {
|
||||||
toast.add({
|
toast.add({
|
||||||
severity: 'success', life: 2000, detail: t('loginSuccessfully'), summary: t('successful'), closable: false
|
severity: "success",
|
||||||
})
|
life: 2000,
|
||||||
setTimeout(() => router.push('/'), 300);
|
detail: t("loginSuccessfully"),
|
||||||
|
summary: t("successful"),
|
||||||
|
closable: false,
|
||||||
|
});
|
||||||
|
setTimeout(() => router.push("/"), 300);
|
||||||
|
} else {
|
||||||
|
return toast.add({
|
||||||
|
severity: "error",
|
||||||
|
life: 2000,
|
||||||
|
detail: t("شما مجوز ورود ندارید"),
|
||||||
|
summary: t("error"),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// // setTimeout(() => router.push('/'), 300);
|
// // setTimeout(() => router.push('/'), 300);
|
||||||
// console.log(data);
|
// console.log(data);
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
<Panel toggleable>
|
<Panel toggleable>
|
||||||
<template #header >
|
<template #header >
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Avatar :image="item.createdBy.avatarMedia" size="large" shape="circle" />
|
<Avatar v-if="item.createdBy.avatarMedia" :image="item.createdBy.avatarMedia" size="large" shape="circle" />
|
||||||
<span class="font-bold">{{item.createdBy.name}}</span>
|
<Avatar v-else image="/src/assets/alt.png" size="large" shape="circle" />
|
||||||
|
<span v-if="item.createdBy.name" class="font-bold">{{item.createdBy.name}}</span>
|
||||||
|
<span v-else class="font-bold">کاربر بدون نام</span>
|
||||||
<Tag v-if="item.createdBy.roles[0] === 'Admin'" class="gap-1" icon="pi pi-user" severity="info" value="ادمین"></Tag>
|
<Tag v-if="item.createdBy.roles[0] === 'Admin'" class="gap-1" icon="pi pi-user" severity="info" value="ادمین"></Tag>
|
||||||
<Tag v-else class="gap-1" icon="pi pi-user" severity="success" value="کاربر"></Tag>
|
<Tag v-else class="gap-1" icon="pi pi-user" severity="success" value="کاربر"></Tag>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,19 +48,19 @@ import { jDate } from "@/utils/jDate";
|
|||||||
const store = useTicketsStore();
|
const store = useTicketsStore();
|
||||||
const messages = ref([]);
|
const messages = ref([]);
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
console.log(route.params.id);
|
|
||||||
const send = async (value) => {
|
const send = async (value) => {
|
||||||
console.log(value);
|
|
||||||
const sended = await store.reply({
|
const sended = await store.reply({
|
||||||
content: value,
|
content: value,
|
||||||
ticketId: route.params.id,
|
ticketId: route.params.id,
|
||||||
});
|
});
|
||||||
console.log('sended', sended);
|
|
||||||
};
|
};
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
const data = await store.ticket(route.params.id);
|
const data = await store.ticket(route.params.id);
|
||||||
messages.value = data.ticketMessages.reverse()
|
messages.value = data.ticketMessages.reverse()
|
||||||
console.log("mess", data);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -47,17 +47,17 @@ const data = ref([])
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const chat = (data) =>{
|
const chat = (data) =>{
|
||||||
router.push(`/tickets/${data.id}`)
|
router.push(`/tickets/${data.id}`)
|
||||||
console.log(data.id);
|
|
||||||
}
|
}
|
||||||
const closeTicket = async(data) =>{
|
const closeTicket = async(data) =>{
|
||||||
// console.log(data.id);
|
// console.log(data.id);
|
||||||
const closess = await store.close(data.id)
|
const closess = await store.close(data.id)
|
||||||
console.log('close',closess);
|
|
||||||
await store.index()
|
await store.index()
|
||||||
}
|
}
|
||||||
watchEffect(async() =>{
|
watchEffect(async() =>{
|
||||||
const { page = 1, pageSize = 10 } = route.query;
|
const { page = 1, pageSize = 10 } = route.query;
|
||||||
data.value = await store.index(page, pageSize)
|
data.value = await store.index(page, pageSize)
|
||||||
console.log(data.value[1]);
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Panel :header="$t('users')">
|
<Panel :header="$t('users')">
|
||||||
<DataTable :value="store.items">
|
<DataTable :value="store.items.user">
|
||||||
<Column class="w-16">
|
<Column class="w-16">
|
||||||
<template #body="{ data: { avatarMedia } }">
|
<template #body="{ data: { avatarMedia } }">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
|
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<Pagination :total="store.total" />
|
<Pagination :total="store.items.count" />
|
||||||
</template>
|
</template>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</Panel>
|
</Panel>
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
import Pagination from "@/components/Pagination.vue";
|
import Pagination from "@/components/Pagination.vue";
|
||||||
import { useUsersStore } from "@/stores/users";
|
import { useUsersStore } from "@/stores/users";
|
||||||
import { jDate } from "@/utils/jDate";
|
import { jDate } from "@/utils/jDate";
|
||||||
console.log("users", useUsersStore);
|
|
||||||
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue";
|
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
@@ -82,5 +82,5 @@ watchEffect(async () => {
|
|||||||
await store.index(page, pageSize);
|
await store.index(page, pageSize);
|
||||||
const pages = await store.page();
|
const pages = await store.page();
|
||||||
});
|
});
|
||||||
console.log(store);
|
|
||||||
</script>
|
</script>
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
|
|
||||||
|
|
||||||
const remove = (data) => {
|
const remove = (data) => {
|
||||||
console.log('done', data._id)
|
|
||||||
confirm.require({
|
confirm.require({
|
||||||
message: t('Are you sure you want to proceed?'),
|
message: t('Are you sure you want to proceed?'),
|
||||||
header: t('Danger Zone'),
|
header: t('Danger Zone'),
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
defaultFocus: 'reject',
|
defaultFocus: 'reject',
|
||||||
accept: async () => {
|
accept: async () => {
|
||||||
const deleted = await store.delete(data._id)
|
const deleted = await store.delete(data._id)
|
||||||
console.log(deleted);
|
|
||||||
await store.index()
|
await store.index()
|
||||||
}
|
}
|
||||||
// product.removing = true
|
// product.removing = true
|
||||||
@@ -165,15 +165,9 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
watchEffect(async() => {
|
watchEffect(async() => {
|
||||||
const { page = 1, pageSize = 10 } = route.query
|
const { page = 1, pageSize = 10 } = route.query
|
||||||
await store.index(page, pageSize)
|
await store.index(page, pageSize)
|
||||||
console.log(store.items[2].image);
|
|
||||||
console.log(String.fromCharCode(store.items[2].image)).join('');
|
|
||||||
// store.items.forEach((product) => {
|
|
||||||
// product.status = { value: t('active'), severity: 'success' };
|
|
||||||
// })
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<Panel :header="$t('تاریخچه ی تخفیف ها')">
|
||||||
|
<DataTable :value="store.items.discount">
|
||||||
|
<Column field="code" :header="$t('کد تخفیف')" />
|
||||||
|
<Column
|
||||||
|
:field="({ admin }) => admin.name"
|
||||||
|
:header="$t('سازنده')"
|
||||||
|
/>
|
||||||
|
<Column :field="({startDate}) => jDate(startDate)" :header="$t('تاریخ شروع')" />
|
||||||
|
<Column :field="({endDate}) => jDate(endDate)" :header="$t('تاریخ پایان')" />
|
||||||
|
<Column
|
||||||
|
field="statusText"
|
||||||
|
:header="$t('status')"
|
||||||
|
/>
|
||||||
|
<template #empty>
|
||||||
|
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
|
||||||
|
</template>
|
||||||
|
</DataTable>
|
||||||
|
</Panel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { watchEffect } from "vue";
|
||||||
|
import { useDiscountStore } from "../stores/discounts";
|
||||||
|
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