1. Add basic course information and improve
1.1 integrated text editor
Copy the contents of the two folders in the figure below from the teaching resources to the folder directory corresponding to the project (copy the component of the teaching resources and timymce under static to the component of the project. If the teaching resources are not downloaded, you can also find the corresponding files through the Vue element admin master downloaded earlier).
(1) Configure html variables
In the configuration file \ build \ webpack Dev.conf.js is configured as follows: the html variable is configured so that the project can find the path of the plug-in just copied.
new HtmlWebpackPlugin({ ......, templateParameters: { BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory } })
(2) Import script
In index Add the following parts to the HTML file. The introduction of Chinese package will be popular, but it will not affect the operation of the framework itself.
<script src=<%=BASE_URL %>/tinymce4.7.5/tinymce.min.js></script> <script src=<%=BASE_URL %>/tinymce4.7.5/langs/zh_CN.js></script>
(3) In our info Using plug-ins in Vue
Let's introduce this plug-in first.
//Introduce Tinymce rich text editor component import Tinymce from '@/components/Tinymce'; export default { .... components: { Tinymce }, }
Then replace the ui component of the previous course introduction.
<!-- Course introduction--> <el-form-item label="Course introduction"> <tinymce :height="300" v-model="courseInfo.description"/> </el-form-item>
Finally, add a style at the end of the file to adjust the height of the button for uploading pictures. The keyword scoped means that the style is only valid on the current page.
<style scoped> .tinymce-container { line-height: 29px; } </style>
The final effect is shown in the figure below.
Now add a complete course, and the test effect is as follows.
The data was successfully added, but the subject_parent_id does not appear to have a value. This is because the data passed by the back-end interface does not have this attribute in the CourseInfoForm. Add the following.
@ApiModelProperty(value = "Primary classification ID") private String subjectParentId;
Note that the attribute name should be consistent with the use of the front end (refer to the figure below).
Retest to verify that the bug has been resolved.
2. Curriculum management
The course outline is also a list, which is basically similar to the function of the previous course classification list.
2.1 backend implementation
(1) Create entity class
com.wangzhou.eduservice.entity.chapter.ChapterVo
/** * Course chapters */ @Data public class ChapterVo { private String id; private String title; private List<VideoVo> children = new ArrayList<>(); }
com.wangzhou.eduservice.entity.chapter.VideoVo
/** * Course section */ @Data public class VideoVo { private String id; private String title; }
(2)Controller
/** * <p> * Course front end controller * </p> * * @author wangzhou * @since 2021-11-12 */ @RestController @RequestMapping("/eduservice/edu-chapter") public class EduChapterController { @Autowired private EduChapterService eduChapterService; /** * Obtain the chapters and sections of the course according to the course id * @return */ @GetMapping("/getChapterVideo/{courseId}") public R getChapterVideo(@PathVariable String courseId) { List<ChapterVo> list = eduChapterService.getChapterVideo(courseId); return R.ok().data("list", list); } }
(3)Service
/** * <p> * Course service implementation class * </p> * * @author wangzhou * @since 2021-11-12 */ @Service public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService { @Autowired private EduVideoService eduVideoService; @Override public List<ChapterVo> getChapterVideo(String courseId) { // 1. Check all chapters according to the course id QueryWrapper<EduChapter> chapterWrapper = new QueryWrapper<>(); chapterWrapper.eq("course_Id", courseId); List<EduChapter> chapterList = baseMapper.selectList(chapterWrapper); // 2. Check all sections according to the id course QueryWrapper<EduVideo> videoWrapper = new QueryWrapper<>(); videoWrapper.eq("course_Id", courseId); List<EduVideo> videoList = eduVideoService.list(videoWrapper); // 3. Traverse all course chapters for encapsulation List<ChapterVo> finalChapterList = new ArrayList<>(); for (int i = 0; i < chapterList.size(); i++) { EduChapter eduChapter = chapterList.get(i); ChapterVo chapterVo = new ChapterVo(); BeanUtils.copyProperties(eduChapter, chapterVo); // 4. Traverse all course sections for encapsulation List<VideoVo> videoVoList = new ArrayList<>(); for (int j = 0; j < videoList.size(); j++) { EduVideo eduVideo = videoList.get(j); if(eduVideo.getChapterId().equals(eduChapter.getId())) { VideoVo videoVo = new VideoVo(); BeanUtils.copyProperties(eduVideo, videoVo); videoVoList.add(videoVo); } } chapterVo.setChildren(videoVoList); finalChapterList.add(chapterVo); } return finalChapterList; } }
(3) Testing
Create some data in the database. The test results using swagger UI are as follows.
{ "success": true, "code": 20000, "message": "success", "data": { "list": [ { "id": "1181729226915577857", "title": "Chapter VII: I/O flow", "children": [ { "id": "1189471423678939138", "title": "test" }, { "id": "1189476403626409986", "title": "22" } ] }, { "id": "15", "title": "Chapter I: Java introduction", "children": [ { "id": "17", "title": "Section I: Java brief introduction" }, { "id": "18", "title": "Section 2: expressions and assignment statements" }, { "id": "19", "title": "Section III: String class" }, { "id": "20", "title": "Section IV: procedural style" } ] }, { "id": "32", "title": "Chapter 2: console input and output", "children": [ { "id": "1182499307429339137", "title": "Section I" } ] }, { "id": "44", "title": "Chapter 3: control flow", "children": [ { "id": "1189434737808990210", "title": "test" } ] }, { "id": "48", "title": "Chapter IV: definition of class", "children": [] }, { "id": "63", "title": "Chapter 5: array", "children": [] }, { "id": "64", "title": "Chapter VI: Inheritance", "children": [] } ] } }
2.2 front end implementation
We can use the previous course list method to implement the front end, that is, we can directly use the template. Let's write the bottom layer to realize the tree structure display of the course outline.
Create a new chapter js.
export default { // Obtain chapter and subsection information according to the course id getChapterVideo(courseId) { return request({ url: '/eduservice/edu-chapter/getChapterVideo' + courseId, method: 'get' }) } }
chapter.vue.
<script> import chapter from "@/api/edu/chapter.js"; export default { data() { return { saveBtnDisabled: false, chapterVideoList:[], courseId:'' }; }, methods: { //Jump to the previous step previous() { this.$router.push({ path: "/course/info/1" }); }, next() { //Jump to step 3 this.$router.push({ path: "/course/publish/1" }); }, getChapterVideo(){ chapter.getChapterVideo(this.courseId) .then(resp => { this.chapterVideoList = resp.data.list }) } }, created() { if(this.$route.params && this.$route.params.id) { this.courseId = this.$route.params.id } this.getChapterVideo() }, }; </script>
Add ui components to display data.
.<template> <div class="app-container"> <h2 style="text-align: center">Publish new courses</h2> <el-steps :active="2" process-status="wait" align-center style="margin- bottom: 40px;" > <el-step title="Fill in the basic information of the course" /> <el-step title="Create syllabus" /> <el-step title="Final release" /> </el-steps> <ul> <li v-for="chapter in chapterVideoList" :key="chapter.id"> <p> {{ chapter.title }} </p> <ul> <li v-for="video in chapter.children" :key="video.id"> {{ video.title }} </li> </ul> </li> </ul> <el-form label-width="120px"> <el-form-item> <el-button @click="previous">Previous step</el-button> <el-button :disabled="saveBtnDisabled" type="primary" @click="next" >next step</el-button > </el-form-item> </el-form> </div> </template>
Remember to add @ CrossOrigin annotation to the back-end interface to solve the cross domain problem. The test results are as follows.
Finally, you can add a little style.
.<template> <div class="app-container"> <h2 style="text-align: center">Publish new courses</h2> <el-steps :active="2" process-status="wait" align-center style="margin- bottom: 40px;" > <el-step title="Fill in the basic information of the course" /> <el-step title="Create syllabus" /> <el-step title="Final release" /> </el-steps> <ul> <li v-for="chapter in chapterVideoList" :key="chapter.id"> <p> {{ chapter.title }} <span> <el-button type="text">Add session</el-button> <el-button style="" type="text">edit</el-button> <el-button type="text">delete</el-button> </span> </p> <!-- video --> <ul class="chapterList videoList"> <li v-for="video in chapter.children" :key="video.id"> <p> {{ video.title }} <span class="acts"> <el-button type="text">edit</el-button> <el-button type="text">delete</el-button> </span> </p> </li> </ul> </li> </ul> <el-form label-width="120px"> <el-form-item> <el-button @click="previous">Previous step</el-button> <el-button :disabled="saveBtnDisabled" type="primary" @click="next" >next step</el-button > </el-form-item> </el-form> </div> </template>
3. Modify course functions
Click the previous step in the figure below to return to the course addition page and modify the course. The following is to realize this process.
The business is actually very simple: write back the data and update the written back data to the database.
3.1 backend implementation
To implement two interfaces. (1) Query the basic information of the course according to the course id, (2) modify the basic course information.
CourseController
// Query course information @GetMapping("/getCourseInfo/{courseId}") public R getCourseInfo(@PathVariable String courseId) { CourseInfoForm courseInfoForm= eduCourseService.getCourseInfo(courseId); return R.ok().data("courseInfoForm", courseInfoForm); } // Modify course information @PostMapping("/updateCourseInfo") public R updateCourseInfo(@RequestBody CourseInfoForm courseInfoForm) { eduCourseService.updateCourseInfo(courseInfoForm); return R.ok(); }
EduCourseServiceImpl
@Override public CourseInfoForm getCourseInfo(String courseId) { CourseInfoForm courseInfoForm = new CourseInfoForm(); // 1. Query Curriculum EduCourse eduCourse= baseMapper.selectById(courseId); BeanUtils.copyProperties(eduCourse, courseInfoForm); // 2. Query description table EduCourseDescription eduCourseDescription = eduCourseDescriptionService.getById(courseId); courseInfoForm.setDescription(eduCourseDescription.getDescription()); return courseInfoForm; } @Override public void updateCourseInfo(CourseInfoForm courseInfoForm) { // 1. Revise the curriculum EduCourse eduCourse = new EduCourse(); BeanUtils.copyProperties(courseInfoForm, eduCourse); int update = baseMapper.updateById(eduCourse); if(update <= 0) { throw new GuliException(20001, "Failed to modify course"); } // 2. Modify description table EduCourseDescription eduCourseDescription = new EduCourseDescription(); eduCourseDescription.setDescription(courseInfoForm.getDescription()); eduCourseDescriptionService.updateById(eduCourseDescription); }
3.2 front end implementation
course.js
//Query basic course information according to course id getCourseInfoById(courseId){ return request({ url:`/eduservice/edu-course/getCourseInfo/${courseId}`, method: 'get', }) }, //Modify course information updateCourseInfo(courseInfoForm){ return request({ url:"/eduservice/edu-course/updateCourseInfo", method: 'post', data: courseInfoForm, }) }
info.vue modifies the jump url.
//Jump to the previous step previous() { this.$router.push({ path: "/course/info/" + this.courseId }); }, next() { //Jump to step 3 this.$router.push({ path: "/course/publish/"+ this.courseId }); } }
Finally, in info Data write back is implemented in Vue.
methods: { getCourseInfo() { course.getCourseInfoById(this.courseId) .then((resp) => { this.courseInfoform = resp.data.courseInfoForm // Note the case here. The front-end naming is not standardized, which leads to this small problem }) } }, created() { if(this.$route.params && this.$route.params.id) { this.courseId = this.$route.params.id this.getCourseInfo() } }
Testing. It is found that according to a small bug: the secondary classification of the course displays the id.
What's going on? The default display principle of drop-down list data is: compare the id of the list storing drop-down options with the id to be displayed by default one by one, and write back the data in the matching list. The primary classification initializes subjectOneList by default in the page, but the secondary classification subjectTwoList does not. The following is a specific solution. You can better understand this process by looking at the code.
methods: { getCourseInfo() { course.getCourseInfoById(this.courseId) .then((resp) => { // Get current course information this.courseInfoform = resp.data.courseInfoForm // Query all level-1 course classifications and initialize subjectOneList subject.getAllSubject() .then((response) => { this.subjectOneList = response.data.list for(var i = 0; i<this.subjectOneList.length; i++) { // Obtain the primary course classification id from the current course information, and compare it with all course classification IDs. If it is equal, all secondary classification IDS will be displayed var oneSubject = this.subjectOneList[i] if(oneSubject.id == this.courseInfoform.subjectParentId) { this.subjectTwoList = oneSubject.children } } }) }) } }, created() { // modify if(this.$route.params && this.$route.params.id) { this.courseId = this.$route.params.id this.getCourseInfo() this.getListTeacher() } else{ // add to this.getListTeacher() this.getOneSubject() } }
There is also a small bug: click add course after echoing the data in the previous step, and the echoed data is still there. Let's solve it.
watch: { $route(to, from) { //Route change mode. When the route sending changes, the method is executed console.log("watch $route"); this.courseInfo={} } }
Finally, adjust saveOrUpdate.
addCourse() { course.addCourseInfo(this.courseInfoform).then(resp => { this.$message({ message: "Successfully added course information", type: "success", }) //Jump to step 2 with the id generated by the course this.$router.push({ path: "/course/chapter/"+resp.data.courseId }); }); }, updateCourse() { course.updateCourseInfo(this.courseInfoform).then(resp => { this.$message({ message: "Course information modified successfully", type: "success", }) //Jump to step 2 and take the id generated by this course. Note that the back end does not return id when updating, but we can get it in the following way this.$router.push({ path: "/course/chapter/"+ this.courseId }); }); }, saveOrUpdate() { if(this.courseInfoform.id) { this.updateCourse() } else { this.addCourse() } },
4. Addition, deletion and modification of course chapters
4.1 adding course chapters
(1)UI
Make a small button.
<el-button type="text" @click="editChapter(chapter.id)">edit</el-button>
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (IMG iqjmncab-1641641538981) (C: / users / 24724 / appdata / roaming / typora user images / image-20211207210533035. PNG)]
The button effect is as follows, ugly.
Then click Add to display the form for adding course information. The ui of the form is as follows.
<!-- Add and modify chapter forms --> <el-dialog :visible.sync="dialogChapterFormVisible" title="Add chapter"> <el-form :model="chapter" label-width="120px"> <el-form-item label="head word"> <el-input v-model="chapter.title"/> </el-form-item> <el-form-item label="Chapter sorting"> <el-input-number v-model="chapter.sort" :min="0" controls-position="right"/> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogChapterFormVisible = false">Cancel</el-button> <el-button type="primary" @click="saveOrUpdate">determine</el-button> </div> </el-dialog>
The dialogChapterFormVisible above is used to control the display and hiding of pop ups. We set its default value to false in data. The chapter is used above, and the next initialization is also performed.
data() { return { saveBtnDisabled: false, chapterVideoList:[], courseId:'', dialogChapterFormVisible:false, // The chapter pop-up box is set to off by default chapter:{} // Packaging chapter }; }
Finally, modify the previously edited button and bind the dialogChapterFormVisible property.
<el-button type="text" @click="dialogChapterFormVisible = true">Add chapter</el-button>
The test results are as follows.
(2) Back end
By the way, the interface for adding, deleting, modifying and querying course chapters is written at one time. EduChapterController.
@PostMapping("/addChapter") public R addChapter(@RequestBody EduChapter chapter) { eduChapterService.save(chapter); return R.ok(); } @GetMapping("/getChapter/{courseId}") public R getChapterById(@PathVariable String courseId) { EduChapter eduChapter= eduChapterService.getById(courseId); return R.ok().data("chapter", eduChapter); } @PostMapping("/updateChapter") public R updateChapter(@RequestBody EduChapter chapter) { eduChapterService.updateById(chapter); return R.ok(); } @DeleteMapping("/deleteChapter") public R deleteChapter(@PathVariable String courseId) { Boolean flag = eduChapterService.removeById(courseId); if(flag) { return R.ok(); } else { return R.error(); } }
The above interface looks good, but there is another small problem. When deleting a chapter, we directly call the removeById method, so that the chapter is indeed deleted, but the section of the chapter is in an awkward position: it no longer belongs to any section (as shown in the figure below). That won't work.
In fact, in the actual development, we need to implement the deleted business logic in the service layer for the problem similar to deleting the primary classification in the primary and secondary classification. We usually have two kinds of implementation logic:
- When deleting a section, the following sections will be deleted together
- If there is a section, it is not allowed to delete the section. You must delete the section after the section is deleted
The second logic is implemented below.
Then the controller changes the name of the called service method.
Implementation method of EduChapterServiceImpl
@Override public Boolean deleteChapter(String courseId) { QueryWrapper<EduVideo> wrapper = new QueryWrapper<>(); wrapper.eq("course_id", courseId); // You only need to judge whether there is a video, but you don't really need to get a video int count = eduVideoService.count(wrapper); if(count > 1) { throw new GuliException(20001, "Cannot delete"); } else { int delete = baseMapper.deleteById(courseId); return delete > 0; } }
(3) Front end interface
chapter.js.
// Add chapter addChapter(chapter) { return request({ url: '/eduservice/edu-chapter/addChapter/', method: 'post', data: chapter }) }, // Query chapter by id getChapter(courseId) { return request({ url: '/eduservice/edu-chapter/getChapter/' + courseId, method: 'get' }) }, // Modify chapter updateChapter(chapter) { return request({ url: '/eduservice/edu-chapter/updateChapter/', method: 'post', data: chapter }) }, deleteChapter(courseId) { return request({ url: '/eduservice/edu-chapter/deleteChapter/' + courseId, method: 'delete' }) }
(4) Front end calling interface display data
Implement the saveOrUpdate method for submitting the form bound by the OK button in the figure below.
chapter.vue
saveOrUpdate() { chapter.addChapter(this.chapter) .then(resp => { // 1. Close the pop-up box this.dialogChapterFormVisible = false // 2. Prompt success this.$message({ message: "Added Course Chapter successfully", type: "success", }) // 3. Refresh the page (just re query the data) this.getChapterVideo() }) }
Is it feeling: simple, boring and boring. Test.
An error was reported. Obviously, it was reported by the back end. Look at the back end. Tell us that courseid is not given a default value.
Look at the database. The courseId must pass values.
The chapter in the data does not give the default value of courseId.
We actually got the courseId before.
Therefore, we can encapsulate the data into EduChapter before transmitting it to EduChapter.
saveOrUpdate() { this.chapter.courseId = this.courseId, chapter.addChapter(this.chapter) .then(resp => { // 1. Close the pop-up box this.dialogChapterFormVisible = false // 2. Prompt success this.$message({ message: "Added Course Chapter successfully", type: "success", }) // 3. Refresh the page (just re query the data) this.getChapterVideo() }) } }
Also, remember to annotate the time related attributes in the back-end EduChapter.
Another test will succeed.
There is also a small bug. When clicking Add chapter the second time, the previously added form data is not cleared. The solution is as follows. The event @ Click is changed to the binding method, and the data is cleared in the method.
<el-button type="text" @click="openChapterDialog">Add chapter</el-button>
openChapterDialog() { this.dialogChapterFormVisible = true, this.chapter.title = '' this.chapter.sort = 0 } }
4.2 modifying course chapters
Note that after adding chapters, there are editing options in the displayed course chapters. This is the ui we implemented before. Now let's implement the functions of this ui.
Bind the Edit button to the method.
Realize the modification function.
saveChapter() { this.chapter.courseId = this.courseId, chapter.addChapter(this.chapter) .then(resp => { // 1. Close the pop-up box this.dialogChapterFormVisible = false // 2. Prompt success this.$message({ message: "Added Course Chapter successfully", type: "success", }) // 3. Refresh the page (just re query the data) this.getChapterVideo() }) }, openChapterDialog() { this.dialogChapterFormVisible = true, this.chapter.title = '' this.chapter.sort = 0 }, openEditChapter(chapterId) { this.dialogChapterFormVisible = true, chapter.getChapter(chapterId) .then(resp => { this.chapter = resp.data.chapter }) }, //Modify chapter updateChapter() { //Set the course id into the chapter object this.chapter.courseId = this.courseId; chapter.updateChapter(this.chapter).then((resp) => { //Close the bullet box this.dialogChapterFormVisible = false; //Prompt information this.$message({ message: "Chapter modified successfully", type: "success", }); //Refresh page this.getChapterVideo(); }); }, saveOrUpdate() { if (this.chapter.id) { //Modify chapter this.updateChapter(); } else { //New chapter this.saveChapter(); } }
4.3 deleting course chapters
//Delete chapter removeById(chapterId) { this.$confirm("This operation will permanently delete the chapter information, Continue?", "Tips", { confirmButtonText: "determine", cancelButtonText: "cancel", type: "warning", }).then(() => { //Click OK to execute the then method chapter.deleteChapter(chapterId).then((resp) => { //Delete succeeded //Prompt information this.$message({ type: "success", message: "Delete succeeded!", }); //Refresh page this.getChapterVideo(); }); });
5. Addition, deletion, modification and query of course sections
Let's implement the back-end interface first.
/** * <p> * Course video front end controller * </p> * * @author wangzhou * @since 2021-11-12 */ @RestController @RequestMapping("/eduservice/edu-video") @CrossOrigin public class EduVideoController { @Autowired private EduVideoService service; // Add section @PostMapping("/addVideo") public R addVideo(@RequestBody EduVideo video) { service.save(video); return R.ok(); } // Delete a section Todo when deleting a section, you should also delete the corresponding video under the section @DeleteMapping("/deleteVideo{videoId}") public R deleteVideo(@PathVariable String videoId) { Boolean flag = service.removeById(videoId); if(flag) { return R.ok(); } else { return R.error(); } } // Find section @GetMapping("/getVideoById{videoId}") public R getVideoById(@PathVariable String videoId) { EduVideo video = service.getById(videoId); return R.ok().data("video", video); } // Modify section @PostMapping("/updateVideo") public R updateVideo(@RequestBody EduVideo video) { service.updateById(video); return R.ok(); } }
Friendly tips, remember to add the @ TableField annotation in EduVideo.
The next step is to implement the lower interface of the front end and create a new video js.
import request from '@/utils/request' export default { // Add section addVideo(video) { return request({ url: '/eduservice/edu-video/addVideo/', method: 'post', data: video }) }, // Query section by id getVideo(videoId) { return request({ url: '/eduservice/edu-video/getVideoById/' + videoId, method: 'get' }) }, // Modify section updateVideo(video) { return request({ url: '/eduservice/edu-video/updateVideo/', method: 'post', data: video }) }, // Delete Measures deleteVideo(videoId) { return request({ url: '/eduservice/edu-video/deleteVideo/' + videoId, method: 'delete' }) } }
At present, it is a very simple and ordinary operation. Then, in the front end, the chapter Vue start calling the interface to realize the function.
.<template> <div class="app-container"> <h2 style="text-align: center">Publish new courses</h2> <el-steps :active="2" process-status="wait" align-center style="margin- bottom: 40px;" > <el-step title="Fill in the basic information of the course" /> <el-step title="Create syllabus" /> <el-step title="Final release" /> </el-steps> <ul> <li v-for="chapter in chapterVideoList" :key="chapter.id"> <p> {{ chapter.title }} <span> <el-button type="text" @click="openVideoDialog(chapter.id)">Add session</el-button> <el-button style="" type="text" @click="openEditChapter(chapter.id)">edit</el-button> <el-button type="text" @click ="removeById(chapter.id)">delete</el-button> </span> </p> <!-- video --> <ul class="chapterList videoList"> <li v-for="video in chapter.children" :key="video.id"> <p> {{ video.title }} <span class="acts"> <el-button type="text" @click="openVideoDialog(chapter.id)">edit</el-button> <el-button type="text" @click="removeVideo">delete</el-button> </span> </p> </li> </ul> </li> </ul> <el-button type="text" @click="openChapterDialog">Add chapter</el-button> <!-- Add and modify chapter forms --> <el-dialog :visible.sync="dialogChapterFormVisible" title="Add chapter"> <el-form :model="chapter" label-width="120px"> <el-form-item label="head word"> <el-input v-model="chapter.title"/> </el-form-item> <el-form-item label="Chapter sorting"> <el-input-number v-model="chapter.sort" :min="0" controls-position="right"/> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogChapterFormVisible = false">Cancel</el-button> <el-button type="primary" @click="saveOrUpdate">determine</el-button> </div> </el-dialog> <!--Add section form--> <!-- Add and modify session forms --> <el-dialog :visible.sync="dialogVideoFormVisible" title="Add session"> <el-form :model="video" label-width="120px"> <el-form-item label="Class title"> <el-input v-model="video.title" /> </el-form-item> <el-form-item label="Class ranking"> <el-input-number v-model="video.sort" :min="0" controls- position="right" /> </el-form-item> <el-form-item label="Is it free"> <el-radio-group v-model="video.free"> <el-radio :label="true">free</el-radio> <el-radio :label="false">default</el-radio> </el-radio-group> </el-form-item> <el-form-item label="Upload video"> <!-- TODO --> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogVideoFormVisible = false">Cancel</el-button> <el-button :disabled="saveVideoBtnDisabled" type="primary" @click="saveOrUpdateVideo()" >determine</el-button > </div> </el-dialog> <el-form label-width="120px"> <el-form-item> <el-button @click="previous">Previous step</el-button> <el-button :disabled="saveBtnDisabled" type="primary" @click="next" >next step</el-button > </el-form-item> </el-form> </div> </template> <script> import chapter from "@/api/edu/chapter.js"; import video from "@/api/edu/video.js"; export default { data() { return { saveBtnDisabled: false, chapterVideoList:[], courseId:'', dialogChapterFormVisible:false, // The chapter pop-up box is set to off by default dialogVideoFormVisible:false, // The section pop-up box is set to off by default chapter:{ title:'', sort:0 }, // Packaging chapter video: { title:'', sort:0, free:0, videoSourceId:'' } //Packaging section }; }, methods: { // ------------------Section------------- openVideoDialog(id) { //Clear previous data this.video = {}; //Set chapter id this.video.chapterId = id; //Show pop ups this.dialogVideoFormVisible = true; }, // saveVideoBtnDisabled() { // }, //Add section addVideo() { //Set course id this.video.courseId = this.courseId; video.addVideo(this.video).then((resp) => { //Close the bullet box this.dialogVideoFormVisible = false; //Prompt information this.$message({ message: "Section added successfully", type: "success", }); //Refresh page this.getChapterVideo(); }); }, saveOrUpdateVideo() { if(this.video.id) { this.updateVideorById(this.video.id); } else { this.addVideo(); } }, //Modify section form echo getVideoById(videoId) { //Pop up section pop-up this.dialogVideoFormVisible = true; video.getVideoById(videoId).then((resp) => { this.video = resp.data.video; }); }, //Subsection modification updateVideorById(videoId) { //Set the section id to the video object this.video.id = videoId; video.updateVideo(this.video).then((resp) => { //Close the bullet box this.dialogVideoFormVisible = false; //Prompt information this.$message({ message: "Section modified successfully", type: "success", }); //Refresh page this.getChapterVideo(); }); }, //Delete Measures removeVideo(videoId) { this.$confirm("This action will permanently delete the section information, Continue?", "Tips", { confirmButtonText: "determine", cancelButtonText: "cancel", type: "warning", }).then(() => { //Click OK to execute the then method video.deleteById(videoId).then((resp) => { //Delete succeeded //Prompt information this.$message({ type: "success", message: "Delete succeeded!", }); //Refresh page this.getChapterVideo(); }); }); }, // ------------------Chapter------------- //Jump to the previous step previous() { this.$router.push({ path: "/course/info/" + this.courseId }); }, next() { //Jump to step 3 this.$router.push({ path: "/course/publish/"+ this.courseId }); }, getChapterVideo(){ chapter.getChapterVideo(this.courseId) .then(resp => { this.chapterVideoList = resp.data.list }) }, saveChapter() { this.chapter.courseId = this.courseId, chapter.addChapter(this.chapter) .then(resp => { // 1. Close the pop-up box this.dialogChapterFormVisible = false // 2. Prompt success this.$message({ message: "Added Course Chapter successfully", type: "success", }) // 3. Refresh the page (just re query the data) this.getChapterVideo() }) }, openChapterDialog() { this.dialogChapterFormVisible = true, this.chapter.title = '' this.chapter.sort = 0 }, openEditChapter(chapterId) { this.dialogChapterFormVisible = true, chapter.getChapter(chapterId) .then(resp => { this.chapter = resp.data.chapter }) }, //Modify chapter updateChapter() { //Set the course id into the chapter object this.chapter.courseId = this.courseId; chapter.updateChapter(this.chapter).then((resp) => { //Close the bullet box this.dialogChapterFormVisible = false; //Prompt information this.$message({ message: "Chapter modified successfully", type: "success", }); //Refresh page this.getChapterVideo(); }); }, saveOrUpdate() { if (this.chapter.id) { //Modify chapter this.updateChapter(); } else { //New chapter this.saveChapter(); } }, //Delete chapter removeById(chapterId) { this.$confirm("This operation will permanently delete the chapter information, Continue?", "Tips", { confirmButtonText: "determine", cancelButtonText: "cancel", type: "warning", }).then(() => { //Click OK to execute the then method chapter.deleteChapter(chapterId).then((resp) => { //Delete succeeded //Prompt information this.$message({ type: "success", message: "Delete succeeded!", }); //Refresh page this.getChapterVideo(); }); }); } }, created() { if(this.$route.params && this.$route.params.id) { this.courseId = this.$route.params.id } this.getChapterVideo() }, }; </script> <style> </style>