一,动态章节表单的实现
章节的内容是拿出来且隐藏的,用触发事件点击添加时,才复制一份出来,复制到想复制的地方,复制的一份也是隐藏的,通过替换隐藏的样式才显示出来。 replaceAll替换隐藏的样式。
(如果章节的内容不拿出来,这个情况太复杂了,不好描述,可以选择不隐藏章节的内容试试看是什么情况。)
//添加章节数据的按钮
<button class="btn btn-primary" onclick="addCourseSection()">+添加章</button>
//点击添加时,复制一份章节的数据到这
<div class="tab-content" id="CourseSectionContent">
</div>
//隐藏的章节的内容,点击添加时复制一份到tabContent
<div id="CourseSection4Clone">
<div sid="chapter-clone" class="chapter-section-clone" style="display:none;">
<div class="form-group clearfix">
<label class="control-label">章名称</label>
<div class="col-md-8">
<input type="text" name="chapterName" class="form-control" placeholder="请输入章名称">
</div>
<div class="col-md-1">
<button class="btn btn-primary" onclick="deleteChapter(this)">-删除章</button>
</div>
<div class="col-md-1">
<button class="btn btn-primary" onclick="addSection(this)">+添加节</button>
</div>
</div>
<div id="section4Clone">
<div sid="section-clone" class="form-group clearfix" style="margin-left: 10px">
<label class="control-label">节信息</label>
<div class="col-md-7">
<input type="text" name="sectionName" class="form-control" placeholder="请输入节名称">
<input type="text" name="videoUrl" class="form-control" style="margin: 10px 0px" placeholder="请输入视频链接">
<input type="text" name="time" class="form-control" placeholder="请输入时长(分钟)">
</div>
<div class="col-md-1">
<button class="btn btn-primary" onclick="deleteSection(this)">-删除节</button>
</div>
</div>
</div>
</div>
</div>
1,添加章节信息:通过包含章节样式的id,复制id 里面包含的章节的输入框,得到的是一个dom 对象,通过html 获取 dom 对象的内容,append 到 tabcontent中。通过样式替换显示出复制到 tabContent的章节输入框。
function addCourseSection() {
let divHtml = $('#CourseSection4Clone').clone().html()
divHtml = divHtml.replaceAll("display:none","display:block")
$('#CourseSectionContent').append(divHtml)
}
2,添加节信息:通过 id 复制节信息,得到一个dom 对象,通过html 获取 dom 对象的内容,append到包含章节的样式中,接在节信息后面。
function addSection(el) {
let divHtml = $('#section4Clone').clone().html()
$(el).parent().parent().parent().append(divHtml)
}
3,删除章节信息 节信息:点击时找到父节点,remove 删除章节信息,节信息。不用管id。
删除章节的信息
function deleteChapter(el) {
$(el).parent().parent().parent().remove()
}
删除节信息
function deleteSection(el) {
$(el).parent().parent().remove()
}
二,动态章节表单的获取
1,创建章节的数组,可以返回章节的数组。
在 tabcontent 中找到所有章 sid 包含的div ,遍历这些div,找到input 中name=chapterName的值,判断有值创建章的对象,把chapter name属性与input值对应上,并创建章下节的数组。(要与章节数据结构的 bean 中的变量命名对应上。)
2,找一个个div 中节 sid 包含的div,遍历这些 div,找到input中name= sectionName的值。( videourl,time也是一样)判断有节的名称,创建节的对象,把属性与值对应上,如果获取的time是null就设置成0。
3,把节对象放到章下节的数组,把章节的对象放到章节的数组。
function getCourseSectionData() {
let ChapterSection = []
let chapterDivs = $('#CourseSectionContent').find("div[sid='chapter-clone']")
$.each(chapterDivs,function (i,item) {
let chapterName = $(item).find("input[name='chapterName']").val()
if (!_os.isEmpty(chapterName)){
let chapterObj = {}
chapterObj.chapterName = chapterName
chapterObj.sections = []
let sectionDivs = $(item).find("div[sid='section-clone']")
$.each(sectionDivs,function (i,item) {
let sectionName = $(item).find("input[name='sectionName']").val()
let videoUrl = $(item).find("input[name='videoUrl']").val()
let time = $(item).find("input[name='time']").val()
if (time == null){
time =0
}
if (!_os.isEmpty(sectionName)){
let sectionObj ={}
sectionObj.name = sectionName
sectionObj.videoUrl = videoUrl
sectionObj.time = time
chapterObj.sections.push(sectionObj)
}
})
ChapterSection.push(chapterObj)
}
})
return ChapterSection;
}
二,动态章节表单数据的提交
在保存课程和章节的触发函数中,调用获取章节数据的函数,获取章节数据。通过 json把章节的数据转化成 String类型,作为ajax的参数提交到 controller,在controller中接收String类型的章节数据。
提交章节的数据
<button class="btn btn-primary" onclick="mergeCourseSection()">保存课程信息</button>
function mergeCourseSection() {
let chapterSectionData = getCourseSectionData();
let chapterSections = JSON.stringify(chapterSectionData)
$('#courseForm').ajaxSubmit({
dataType:'json',
data:{'chapterSections':chapterSections},
success:function (resp) {
if (resp.errcode == 0){
Modal.alert('保存成功')
}else {
Modal.alert('保存失败1')
}
},error:function (resp) {
Modal.alert('保存失败2')
}
})
}
}
三,动态章节数据的保存
1,controller中判断能接收到章节的数据,将数据转化成 list 格式,再调用保存章节数据的函数,返回课程的时长。
(这里以只添加和保存章节的数据做说明,那么返回的就是新增的课程时长。如果是保存课程和章节的数据,那么要在一个事务中,先保存课程的数据,再调用保存章节数据的函数。章节的数据是创建在一个课程之下的,所以还要把课程的id获取过来。)
@RequestMapping("/mergeCourseSection")
@ResponseBody
public String mergeCourseSection(Course entity,String coursePicture,String chapterSections){
List<chapterSectionBean> sectionList = null;
if (StringUtils.isNotEmpty(chapterSections)){
sectionList = JSONArray.parseArray(chapterSections,chapterSectionBean.class);
}
}
Integer CourseTotalTime = courseSectionService.createCourseSection(entity.getId(),sectionList);
2,创建章节的数据到数据库。
a 声明课程的总时长,返回课程的总时长。(如果是只添加章节的数据,那么返回的就是新增的课程时长。只是命名成totalCourseTime而已。)
getMaxSort:获取章最大的sort,修改添加章节的数据时,在章最大的 sort 后面自增(对传入的参数的说明:一个课程下所有章的parentId都是0。一个课程一个章下所有节的parentId都是章的id,所以传入课程的id和parentId,结合sql ,就能获取一个课程下的所有章,获一个课程的章下所有节,最大的sort。)如果数据库中获取最大的sort,获取为空,sort就给个默认值,默认从0开始,先自增再设置到对应的字段中。(获取的sort 为空代表该课程下,原先是没有章节的数据的。)
//获取最大的sort,有两个参数传入一个对象
public Integer getMaxSort(Long courseId, Long parentId){
CourseSection entity = new CourseSection();
entity.setCourseId(courseId);
entity.setParentId(parentId);
return entityDao.getMaxSort(entity);
}
课程的章节表
b 先创建章的数据:把章节的数据遍历到bean(因为courseSection表,没有chapterName)使用数据库的映射的类,创建数据库表的数据。
设置courseId,方法中带过来的。
parentId:章的parentId是0,节的parentId是章的id。
name:bean中有chapterName。
设置sort:先自增再设置进去。
在创建章的数据时声明章的时长,先把章的时长设为0,不要为了美观把章的时长变量的声明放到外面。
onsale:因为有课程的上下架功能,所以默认所有的章节都上架。
videoUrl:章没有videoUrl,所以设为空串。
当前时间设为创建和更新的时间。
create方法创建章的数据。
c创建节的数据:
获取节最大的sort,修改添加章节数据时在节最大的sort后面自增。
判断 bean 中有没有节的数据,有就遍历出来,遍历到CourseSection中,用StringUtils判断该有的 sectionName,time,videoUrl,有没有,没有就初始化一下。
章节共用一个表,设置一下其他的字段。
d 把章的总时长算出来。
foreach批量创建小节的数据。
和判断章的总时长不为0,把章的总时长设置进去,再更新章的数据清零变量。
//保存章节的数据到数据库
public Integer createCourseSection(Long courseId,List<chapterSectionBean> sectionList){
Integer CourseTotalTime = 0; //声明课程的时长
//获取章最大的sort,修改时在章数据最大的sort后面自增
Integer maxChapterSort = this.getMaxSort(courseId,0L);
if (null == maxChapterSort)maxChapterSort=0;
//先创建章的数据
for (int i = 0; i < sectionList.size(); i++){
chapterSectionBean chapterSectionBeans = sectionList.get(i); //遍历到bean courseSection表没有chapterName
CourseSection chapter = new CourseSection();
chapter.setCourseId(courseId);
chapter.setParentId(0L);
chapter.setName(chapterSectionBeans.getChapterName());
maxChapterSort += 1;
chapter.setSort(maxChapterSort);
chapter.setTime(0);
Integer ChapterTotalTime = 0; //在创建章的数据时声明章的时长
chapter.setOnsale(1);
chapter.setVideoUrl("");
chapter.setCreateAt(new Date());
chapter.setUpdateAt(new Date());
this.create(chapter);
//获取节最大的sort,修改时在节最大的sort后面自增
Integer maxSectionSort = this.getMaxSort(courseId,chapter.getId());
if (null == maxSectionSort)maxSectionSort=0;
//判断章下有没有节的数据,有遍历出来
if (!CollectionUtils.isEmpty(chapterSectionBeans.getSections())){
for (int j = 0;j < chapterSectionBeans.getSections().size();j++){
CourseSection sections = chapterSectionBeans.getSections().get(j);
//创建节的数据
sections.setCourseId(courseId);
sections.setParentId(chapter.getId());
maxSectionSort += 1;
sections.setSort(maxSectionSort);
sections.setOnsale(1);
//判断如果提交的节的数据为空就初始化一下
if (StringUtils.isEmpty(sections.getName())){
sections.setName("");
}
if (sections.getTime()==null){
sections.setTime(0);
}
if (StringUtils.isEmpty(sections.getVideoUrl())){
sections.setVideoUrl("");
}
sections.setCreateAt(new Date());
sections.setUpdateAt(new Date());
ChapterTotalTime += sections.getTime();//计算章的时长
}
//foreach批量创建所有的小节
this.entityDao.createSections(chapterSectionBeans.getSections()); //之后会在bean里面补充所有CourseSection的字段
//更新章的时间
if (ChapterTotalTime != 0){
chapter.setTime(ChapterTotalTime);
this.update(chapter);
CourseTotalTime += ChapterTotalTime; //更新课程的时间
}
}
}
return CourseTotalTime;
}
批量创建所有小节数据的sql
<insert id="createSections" parameterType="java.util.List">
INSERT INTO course_section
( courseId,parentId,name,sort,time,onsale,videoUrl,createAt,updateAt )
VALUES
<foreach collection="list" item="item" index="index" separator=",">
( #{item.courseId},#{item.parentId},#{item.name},#{item.sort},#{item.time},#{item.onsale},#{item.videoUrl},#{item.createAt},#{item.updateAt} )
</foreach>
</insert>
四,针对时长的处理
a 在修改时选择添加课程的章节数据,要更新对应的课程时长:
创建章节的数据会返回一个新增的课程时长,新增的章节数据创建在一个课程下。
通过课程的 id 获取课程的数据,获取课程的时长,加上传过来的新增的时长,更新课程的数据。(或者之后修改和删除时加上负的时长)获取时都可以加上判断避免空指针异常。
public void updateCourseTime (Long courseId, Integer chapterSectionTime){
Course course = this.getById(courseId);
if (course != null && chapterSectionTime != null){
String time = course.getTime();
if (!StringUtils.isEmpty(time)){
Integer totalTime = Integer.parseInt(time);
totalTime = chapterSectionTime + totalTime ;
course.setTime(totalTime.toString());
this.update(course);
}
}
}
b 修改章节的数据:章只能修改章的名称,修改节信息包含修改小节的时长。
修该章的数据
修改节的信息
根据前端传过来的当前操作的数据的id,获取被修改的数据。
如果修改的是小节的数据,获取修改后的时长差,(也就是原时长减去修改后的时长)
判断如果修改了小节的时间,更新小节的时间,更新章的时间,更新课程的时间。
最后更新数据库中章名称数据。
@Transactional
public void updateSection(CourseSection courseSection){
CourseSection entity = this.getById(courseSection.getId()); //获取原修改的数据
if (0 != entity.getParentId()){ //如果修改的是小节的数据,只有获取的数据才有parentId,courseId
//获取修改后的时长差
Integer time = courseSection.getTime() - entity.getTime();
//如果修改了时间再做接下来的操作
if (time != 0){
//更新小节的时间
entity.setTime(courseSection.getTime());
//更新章的时间
CourseSection chapter = this.getById(entity.getParentId()); //获取章
chapter.setTime(chapter.getTime() + time); //设置章的时长
this.update(chapter); //更新章的数据
//更新课程的时间
courseService.updateCourseTime(entity.getCourseId(),time);
}
}
this.update(entity); //更新数据库的数据
}
c 删除章节的数据:
公共的弹窗发出删除的提示信息,点击确定ajax提交删除操作的id
function deleteSection(id) {
Modal.confirm('确定删除',function () {
$.ajax({
url:'${base}/deleteSection',
dataType:'json',
data:{'id':id},
success:function (resp) {
if (resp.errcode == 0){
window.location.reload();
}else {
Modal.alert('删除失败')
}
}
})
})
}
先根据 id 获取这条要被删除的数据。
更新课程的时长。
删掉对应的数据。
如果删除的是章的数据,要删除章下所有的小节。(删除parentId是章id的小节的数据)
如果删除的是小节的数据,更新章的时长:获取节的父节点,getById获取章,获取章的时长,减掉小节的时长,更新章的数据。
@Transactional
public void deleteSection(Long id){
CourseSection courseSection = entityDao.getById(id);
//不管删除的是章还是节,都要更新课程的时间
//每条章或节的数据都有课程的id,传入课程的id,和删除此条章或节的数据要减掉的时长
courseService.updateCourseTime(courseSection.getCourseId(),-courseSection.getTime());
//删除对应的分类
entityDao.delete(courseSection);
if (courseSection != null){ //判断能不能获取到操作的数据,能获取到再做以下的处理,避免空指针
if (courseSection.getParentId() == 0){ //如果删除的是章,要删除章下的所有小节
entityDao.deleteByParentId(courseSection);
entityDao.update(courseSection);
}else { //如果删除的是节,要更新章的时间
CourseSection chapter = entityDao.getById(courseSection.getParentId()); //获取章的数据
chapter.setTime(chapter.getTime()-courseSection.getTime()); //更新章的时间
entityDao.update(chapter); //更新章的数据
}
}
}