歌手与歌曲关系是一对多,编辑关系只要在有外键的歌曲的页面进行操作。而歌单与歌曲的关系不同与此,为优化用户的体验,不在歌曲的页面实现,应当在一个新的组件中实现。再结合歌曲的数据分析,可以已歌手作为歌曲的分类信息。最后页面设计的实现可以基于el-tree-fransfer (一个基于 VUE 和 element-ui 的树形穿梭框组件)。效果如图所示:
![image-20210416174733569](https://img.kancloud.cn/c6/0b/c60b423e7434d96fd9972fcf6f4f8c6d_1211x775.png)
## 5.3.1 服务端
(1)基于前台需求封装对象。
1)EL穿梭框树对象父节点,关键代码如下:
```java
public class ElNode0Vo implements Serializable {
/**
* 节点Id
*/
private String id;
/**
* 父节点ID
*/
private Long pid = 0L;
/**
* 节点文本
*/
private String label = "";
/**
* 节点是否关闭
*/
private Boolean disabled = false;
/**
* 子节点
*/
private List<ElNodeVo> children;
}
```
2)EL穿梭框树对象子节点
```java
public class ElNodeVo implements Serializable {
/**
* 节点Id
*/
private String id;
/**
* 父节点ID
*/
private String pid;
/**
* 节点文本
*/
private String label = "";
/**
* 节点是否关闭
*/
private Boolean disabled = false;
/**
* 子节点
*/
private List<ElNodeVo> children;
}
```
(2)根据查询到的歌曲查询数据封装为EL穿梭框树对象:
```java
private List<ElNode0Vo> toVoList(List<Song> songList) {
if (CollectionUtils.isEmpty(songList)) {
return null;
}
ArrayList<ElNode0Vo> elNodeVos = new ArrayList<>();
Map<Long, List<Song>> collect = songList.stream().collect(Collectors.groupingBy(Song::getSingerId));
collect.forEach(
(singerId, songs) -> {
String pid = "singer" + singerId;
ElNode0Vo elNode0Vo = new ElNode0Vo();
elNode0Vo.setId(pid);
elNode0Vo.setLabel(iSingerService.getById(singerId).getName());
elNode0Vo.setChildren(songs.stream().map(
song -> {
ElNodeVo nodeVo = new ElNodeVo();
nodeVo.setId(String.valueOf(song.getId()));
nodeVo.setLabel(song.getName());
nodeVo.setPid(pid);
return nodeVo;
}
).collect(Collectors.toList()));
elNodeVos.add(elNode0Vo);
}
);
return elNodeVos;
}
```
(3)根据前端传来的EL穿梭框树对象将数据保存到数据库;
```java
List<ElNode0Vo> toData = in.getToData();
if (CollUtil.isNotEmpty(toData)) {
ArrayList<ListSongMap> listSongMaps = new ArrayList<>();
toData.forEach(
singer -> {
List<ElNodeVo> children =
singer.getChildren();
children.forEach(
song -> {
ListSongMap songMap = new ListSongMap();
songMap.setSongId(Long.valueOf(song.getId()));
songMap.setSongListId(songListId);
listSongMaps.add(songMap);
}
);
}
);
return this.saveBatch(listSongMaps);
}
```
## 5.3.2 后台设计
(1)将编辑页面封装为弹出框
```vue
<el-dialog
:title="name"
:visible.sync="dialogVisible"
width="60%"
:before-close="handleClose">
<tree-transfer :title="title" :from_data='fromData'
:button_text="button_text"
:to_data='toData'
:defaultProps="{label:'label'}"
@addBtn='add' @removeBtn='remove'
:mode='mode' height='540px' filter>
</tree-transfer>
<span slot="footer" class="dialog-footer">
<el-button
@click="resetData">重置</el-button>
<el-button type="primary" @click="onsubmit">提交
</el-button>
</span>
</el-dialog>
```