|
|
|
|
@ -1,5 +1,5 @@ |
|
|
|
|
<template> |
|
|
|
|
<div> |
|
|
|
|
<div id="lilcity__course-redactor" v-on:course_publish="onCoursePublish"> |
|
|
|
|
<div v-if="!courseLoading"> |
|
|
|
|
<form v-if="viewSection !== 'lessons-edit'" @submit.prevent="onSubmit"> |
|
|
|
|
<div class="info"> |
|
|
|
|
@ -21,12 +21,15 @@ |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<div class="info__title"> |
|
|
|
|
<div class="info__field field field_info"> |
|
|
|
|
<div class="info__field field field_info" |
|
|
|
|
v-bind:class="{ error: ($v.course.title.$dirty || showErrors) && $v.course.title.$invalid }"> |
|
|
|
|
<div class="field__label">НАЗВАНИЕ КУРСА</div> |
|
|
|
|
<div class="field__wrap"> |
|
|
|
|
<textarea class="field__textarea field__textarea_lg" |
|
|
|
|
rows="1" |
|
|
|
|
title="Название курса" |
|
|
|
|
v-autosize="course.title" |
|
|
|
|
@input="$v.course.title.$touch()" |
|
|
|
|
v-model="course.title"></textarea> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
@ -39,16 +42,18 @@ |
|
|
|
|
placeholder="Выберите категорию"/> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<div class="info__field field field_info"> |
|
|
|
|
<div class="info__field field field_info" |
|
|
|
|
v-bind:class="{ error: ($v.course.duration.$dirty || showErrors) && $v.course.duration.$invalid }"> |
|
|
|
|
<div class="field__label field__label_gray">ПРОДОЛЖИТЕЛЬНОСТЬ</div> |
|
|
|
|
<div class="field__wrap"> |
|
|
|
|
<input type="text" class="field__input" v-model.number="course.duration"> |
|
|
|
|
<input type="text" class="field__input" v-model.number="course.duration" |
|
|
|
|
@input="$v.course.duration.$touch()"> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<div class="info__field field field_info"> |
|
|
|
|
<div class="field__label field__label_gray">СТОИМОСТЬ</div> |
|
|
|
|
<div class="field__wrap"> |
|
|
|
|
<input type="text" class="field__input" v-model="displayPrice" :disabled="!course.is_paid"> |
|
|
|
|
<input type="text" class="field__input" v-model.number="displayPrice" :disabled="!course.is_paid"> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
@ -116,12 +121,14 @@ |
|
|
|
|
<div class="section__center center"> |
|
|
|
|
<div class="kit"> |
|
|
|
|
<div class="kit__section"> |
|
|
|
|
<div class="kit__field field"> |
|
|
|
|
<div class="kit__field field" |
|
|
|
|
v-bind:class="{ error: ($v.course.short_description.$dirty || showErrors) && $v.course.short_description.$invalid }"> |
|
|
|
|
<div class="field__wrap"> |
|
|
|
|
<textarea type="text" |
|
|
|
|
class="field__input" |
|
|
|
|
<textarea class="field__input" |
|
|
|
|
rows="1" |
|
|
|
|
placeholder="Кратко о курсе" |
|
|
|
|
v-autosize="course.short_description" |
|
|
|
|
@input="$v.course.short_description.$touch()" |
|
|
|
|
v-model="course.short_description"></textarea> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
@ -179,11 +186,11 @@ |
|
|
|
|
|
|
|
|
|
<block-add v-on:added="onBlockAdded"/> |
|
|
|
|
|
|
|
|
|
<div class="kit__foot"> |
|
|
|
|
<!--<div class="kit__foot"> |
|
|
|
|
<button type="submit" class="kit__submit btn btn_md" v-bind:class="{ loading: courseSaving }"> |
|
|
|
|
Сохранить |
|
|
|
|
</button> |
|
|
|
|
</div> |
|
|
|
|
</div>--> |
|
|
|
|
</div> |
|
|
|
|
<div v-if="viewSection === 'lessons'" class="kit__body"> |
|
|
|
|
<div class="lessons__title title">Содержание курса</div> |
|
|
|
|
@ -243,6 +250,8 @@ |
|
|
|
|
import LessonRedactor from "./LessonRedactor"; |
|
|
|
|
import {api} from "../js/modules/api"; |
|
|
|
|
import BlockAdd from "./blocks/BlockAdd"; |
|
|
|
|
import debounce from 'lodash.debounce'; |
|
|
|
|
import {required, minValue, numeric } from 'vuelidate/lib/validators' |
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
|
name: "course-redactor", |
|
|
|
|
@ -315,9 +324,31 @@ |
|
|
|
|
'value': '18:00', |
|
|
|
|
} |
|
|
|
|
], |
|
|
|
|
showErrors: false, |
|
|
|
|
savingTimeout: null, |
|
|
|
|
categoryOptions: [] |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
validations: { |
|
|
|
|
course: { |
|
|
|
|
title: { |
|
|
|
|
required |
|
|
|
|
}, |
|
|
|
|
short_description: { |
|
|
|
|
required |
|
|
|
|
}, |
|
|
|
|
duration: { |
|
|
|
|
required, |
|
|
|
|
numeric, |
|
|
|
|
minValue: minValue(1) |
|
|
|
|
}, |
|
|
|
|
category: { |
|
|
|
|
required, |
|
|
|
|
numeric, |
|
|
|
|
minValue: minValue(1) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
methods: { |
|
|
|
|
onCoverImageSelected(event) { |
|
|
|
|
let file = event.target.files[0]; |
|
|
|
|
@ -386,6 +417,7 @@ |
|
|
|
|
content: [], |
|
|
|
|
}; |
|
|
|
|
this.viewSection = 'lessons-edit'; |
|
|
|
|
window.scrollTo(0, 0); |
|
|
|
|
}, |
|
|
|
|
onSubmit() { |
|
|
|
|
this.courseSaving = true; |
|
|
|
|
@ -425,29 +457,62 @@ |
|
|
|
|
goToLessons() { |
|
|
|
|
this.viewSection = 'lessons'; |
|
|
|
|
}, |
|
|
|
|
loadCourse(courseId) { |
|
|
|
|
loadCourseDraft() { |
|
|
|
|
this.courseLoading = true; |
|
|
|
|
api.loadCourse(courseId, this.accessToken) |
|
|
|
|
api.getCourseDraft(this.accessToken) |
|
|
|
|
.then((response) => { |
|
|
|
|
this.courseLoading = false; |
|
|
|
|
this.course = api.convertCourseJson(response.data); |
|
|
|
|
this.lessons = response.data.lessons.map((lessonJson) => { |
|
|
|
|
return api.convertLessonJson(lessonJson); |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
this.courseLoading = false; |
|
|
|
|
console.log('error course loading', err); |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
loadLessons(courseId) { |
|
|
|
|
api.getCourseLessons(courseId, this.accessToken) |
|
|
|
|
loadCourse() { |
|
|
|
|
this.courseLoading = true; |
|
|
|
|
api.loadCourse(this.courseId, this.accessToken) |
|
|
|
|
.then((response) => { |
|
|
|
|
this.lessons = response.data.results.map((lessonJson) => { |
|
|
|
|
this.courseLoading = false; |
|
|
|
|
this.course = api.convertCourseJson(response.data); |
|
|
|
|
this.lessons = response.data.lessons.map((lessonJson) => { |
|
|
|
|
return api.convertLessonJson(lessonJson); |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
this.courseLoading = false; |
|
|
|
|
console.log('error course loading', err); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
loadLessons(courseId) { |
|
|
|
|
|
|
|
|
|
}, |
|
|
|
|
onCoursePublish() { |
|
|
|
|
console.log('publish course'); |
|
|
|
|
}, |
|
|
|
|
saveCourseDraft: debounce(function (newValue, oldValue) { |
|
|
|
|
if (!oldValue.id) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
this.courseSaving = true; |
|
|
|
|
clearTimeout(this.savingTimeout); |
|
|
|
|
document.getElementById('course-redactor__saving-status').innerText = 'СОХРАНЕНИЕ'; |
|
|
|
|
api.saveCourse(this.course, this.accessToken) |
|
|
|
|
.then((response) => { |
|
|
|
|
this.courseSaving = false; |
|
|
|
|
document.getElementById('course-redactor__saving-status').innerText = 'СОХРАНЕНО'; |
|
|
|
|
this.savingTimeout = setTimeout(() => { |
|
|
|
|
document.getElementById('course-redactor__saving-status').innerText = ''; |
|
|
|
|
}, 2000); |
|
|
|
|
// this.course = api.convertCourseJson(response.data); |
|
|
|
|
}) |
|
|
|
|
.catch((err) => { |
|
|
|
|
this.courseSaving = false; |
|
|
|
|
}); |
|
|
|
|
}, 2000) |
|
|
|
|
}, |
|
|
|
|
mounted() { |
|
|
|
|
api.getCategories(this.accessToken) |
|
|
|
|
@ -464,8 +529,9 @@ |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (this.courseId) { |
|
|
|
|
this.loadCourse(this.courseId); |
|
|
|
|
this.loadLessons(this.courseId); |
|
|
|
|
this.loadCourse() |
|
|
|
|
} else { |
|
|
|
|
this.loadCourseDraft(); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
computed: { |
|
|
|
|
@ -500,7 +566,16 @@ |
|
|
|
|
courseFullUrl() { |
|
|
|
|
let suffix = this.course.url ? this.course.url : 'ваша_ссылка'; |
|
|
|
|
return `https://lil.city/course/${suffix}`; |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
watch: { |
|
|
|
|
'course': { |
|
|
|
|
handler: function (newValue, oldValue) { |
|
|
|
|
this.saveCourseDraft(newValue, oldValue); |
|
|
|
|
}, |
|
|
|
|
deep: true, |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
}, |
|
|
|
|
components: { |
|
|
|
|
BlockAdd, |
|
|
|
|
|