• Django实现音乐网站 ⒆


    使用Python Django框架做一个音乐网站

    本篇主要为排行榜功能及音乐播放器部分功能实现。

    目录

    推荐排行榜优化

    设置歌手、单曲跳转链接

    排行榜列表渲染优化

    视图修改如下:

    模板修改如下:

    单曲详情修改

    排行榜列表

    设置路由

    视图处理

    模板渲染

    设置跳转入口

    播放器功能开发

    设置路由

    模板页面

    脚本渲染

    列表渲染和播放器实现

    音乐播放器列表展示关闭

    总结


    推荐排行榜优化

    设置歌手、单曲跳转链接

    因为歌手、单曲功能页面已开发,现在终于可以设置跳转链接。

    内容如下:

    1. <div class="top_info">
    2. <p class="song_name"><a href="{% url 'player:album_song' %}?sid={{item.id}}">{{ item.name }}a>p>
    3. <p class="singler"><a href="{% url 'player:singer_detail' jitem.singler.id %}">{{ item.singler.name }}a>p>
    4. div>

     

    排行榜列表渲染优化

    之前排行榜是通过视图多个集合变量分别进行渲染,在模板页面要写五个排行版模块,分别进行渲染对应的排行榜,经过尝试后整理到一个总排行榜列表集。

    视图修改如下:
    1. def index(request):
    2. """ 显示首页 """
    3. # 获取首页轮播图
    4. carousels = Carousel.objects.all()
    5. # 推荐歌单 选播放量最多的五个
    6. songsheets = SongSheet.objects.order_by('-playnum').all()[0:5]
    7. # 推荐排行榜
    8. rank_alls = [
    9. # 热歌榜 取播放量最多的五个
    10. Singe.objects.order_by('-playnum').all()[0:5],
    11. # 新歌榜 取最新的五个
    12. Singe.objects.order_by('-id').all()[0:5],
    13. # 飙升榜
    14. Singe.objects.order_by('id').all()[0:5],
    15. # 欧美榜
    16. Singe.objects.order_by('-id').all()[0:5],
    17. # 日韩榜
    18. Singe.objects.order_by('id').all()[0:5]
    19. ]
    20. # 推荐歌手 取单曲最多的六个
    21. singlers = Singler.objects.order_by('-singe_num').all()[0:6]
    22. return render(request, 'index/index.html', locals())

    模板修改如下:

    两套循环嵌套,外循环渲染父盒子,内循环展示排行榜中列表内容;

    通过判断forloop.couter来调用相应排行榜背景图。

    1. <div class="recommend_rank">
    2. <div class="title">
    3. <div class="name">推荐排行榜div>
    4. <ul>
    5. <li><a href="#">更多>a>li>
    6. ul>
    7. div>
    8. <div class="list">
    9. {% for rankitem in rank_alls %}
    10. <div class="bank">
    11. <div class="bank_top">
    12. <div class="img">
    13. <img class="img_tip" src="{% static 'images/' %}b{{forloop.counter}}.png" alt="">
    14. div>
    15. <img class="img_bg" src="{% static 'images/' %}b{{forloop.counter}}_{{forloop.counter}}.jpg" alt="">
    16. div>
    17. <ul class="bank_list">
    18. {% for hot in rankitem %}
    19. <li>
    20. {% if forloop.counter == 1 %}
    21. <div class="top_img top1">div>
    22. {% elif forloop.counter == 2 %}
    23. <div class="top_img top2">div>
    24. {% elif forloop.counter == 3 %}
    25. <div class="top_img top3">div>
    26. {% else %}
    27. <div class="top_index">{{forloop.counter}}div>
    28. {% endif %}
    29. <div class="top_info">
    30. <p class="song_name">
    31. <a href="{% url 'player:album_song' %}?sid={{hot.id}}">{{ hot.name }}a>
    32. p>
    33. <p class="singler">
    34. <a href="{% url 'player:singer_detail' hot.singler.id %}">{{ hot.singler.name }}a>
    35. p>
    36. div>
    37. li>
    38. {% endfor %}
    39. ul>
    40. div>
    41. {% endfor %}
    42. div>
    43. div>

    单曲详情修改

    因为之前单曲详情视图处理为查询相应专辑和单曲信息,

    需要传递两个参数:即专辑id和单曲id。

    但是通过排行榜跳转单曲详情,没有专辑id,故需要修改原来的处理。

    通过单曲反向查询所属专辑信息。

    内容如下:

    1. def album_song(request):
    2. """ 专辑中单曲详情 """
    3. sid = request.GET.get('sid')
    4. song_info = Singe.objects.filter(id=sid).first()
    5. # 反向查询专辑
    6. info = song_info.album_set.first()
    7. # 歌词处理
    8. lyrics = []
    9. if song_info:
    10. lyrics = read_lyric(song_info.lyric)
    11. return render(request, 'album/song.html', locals())

    排行榜列表

    设置路由

    在子应用文件夹中urls.py中新增一条记录。

    1. # 排行榜
    2. path('rank', views.rank, name='rank'),

    视图处理

    榜单列表需要展示各个排行榜所有一定时间段中上榜单曲列表;

    因为前期单曲没有与类型表关联,目前就只能做一个类似的功能。

    下方代码中处理了分页查询;通过id来区别类型进行排序;

    设置了榜单的名称,更新时间是取单曲列表中时间最晚的一个时间。

    1. def rank(request):
    2. """ 排行榜 """
    3. id = int(request.GET.get('id', 1))
    4. page = int(request.GET.get('page', 1))
    5. singe_db = Singe.objects
    6. if id == 1:
    7. song_list = singe_db.order_by('-id').all()
    8. rank_name = '新歌榜'
    9. elif id == 2:
    10. song_list = singe_db.order_by('-playnum').all()
    11. rank_name = '热歌榜'
    12. elif id == 3:
    13. song_list = singe_db.order_by('playnum').all()
    14. rank_name = '飙升榜'
    15. elif id == 4:
    16. song_list = singe_db.order_by('-id').all()
    17. rank_name = '抖音歌曲榜'
    18. elif id == 5:
    19. song_list = singe_db.order_by('id').all()
    20. rank_name = '万物DJ榜'
    21. else:
    22. song_list = singe_db.order_by('-playnum').all()
    23. rank_name = '会员畅听榜'
    24. # 实例化Paginator
    25. page_num = 20
    26. paginator = Paginator(song_list, page_num)
    27. try:
    28. res = paginator.page(page)
    29. except PageNotAnInteger:
    30. res = paginator.page(1)
    31. except EmptyPage:
    32. res = paginator.page(paginator.num_pages)
    33. list_num = len(res)
    34. # 榜单更新时间
    35. updatetime = date.today()
    36. for item in song_list:
    37. if updatetime.ctime() < item.updatetime.ctime():
    38. updatetime = item.updatetime
    39. return render(request, 'rank/index.html', locals())

    模板渲染

    在templates文件夹下创建rank文件夹,并在其中创建index.html文件。

    其他渲染都大同小异,主要有一点新的东西,因为榜单数据比较多,在第一页时候有个前三名标识,需要区别处理第二页之后的序列号和相应标识处理,这里使用了模板中的过滤器。

    内容如下:

    1. {% extends 'common/base.html' %}
    2. {% load static %}
    3. {% block title %}我的音乐{% endblock title %}
    4. {% block content %}
    5. <link rel="stylesheet" href="{% static 'css/rank.css' %}">
    6. <div class="header">
    7. <img src="{% static 'images/logo.png' %}" class="logo" alt="">
    8. <ul>
    9. <li><a href="{% url 'player:index' %}">推荐a>li>
    10. <li><a href="javascript:void(0)" class="selected">排行榜a>li>
    11. <li><a href="{% url 'player:singer' 1 '#' %}">歌手a>li>
    12. <li><a href="{% url 'player:songsheet' %}">歌单a>li>
    13. ul>
    14. div>
    15. <div class="main_con">
    16. <div class="con_l">
    17. <div class="con">
    18. <div class="tabs flex_c">
    19. <span class="active">官方span>
    20. <span class="">特色span>
    21. <span class="">场景span>
    22. div>
    23. <ul class="tab_con">
    24. <li class="flex_c active">
    25. <img alt="" class="cover" data-src="{% static 'images/rank_list_1.png' %}" src="{% static 'images/rank_list_1.png' %}" lazy="loaded">
    26. <div class="item_info">
    27. <p class="name"><a href="{% url 'player:rank' %}?id=1">新歌榜a>p>
    28. <p class="time">今日更新p>
    29. div>
    30. li>
    31. <li class="flex_c active">
    32. <img alt="" class="cover" data-src="{% static 'images/rank_list_2.png' %}" src="{% static 'images/rank_list_2.png' %}" lazy="loaded">
    33. <div class="item_info">
    34. <p class="name"><a href="{% url 'player:rank' %}?id=2">热歌榜a>p>
    35. <p class="time">今日更新p>
    36. div>
    37. li>
    38. <li class="flex_c active">
    39. <img alt="" class="cover" data-src="{% static 'images/rank_list_3.png' %}" src="{% static 'images/rank_list_3.png' %}" lazy="loaded">
    40. <div class="item_info"><p class="name"><a href="{% url 'player:rank' %}?id=3">飙升榜a>p>
    41. <p class="time">今日更新p>
    42. div>
    43. li>
    44. <li class="flex_c active">
    45. <img alt="" class="cover" data-src="{% static 'images/rank_list_4.png' %}" src="{% static 'images/rank_list_4.png' %}" lazy="loaded">
    46. <div class="item_info"><p class="name"><a href="{% url 'player:rank' %}?id=4">抖音歌曲榜a>p>
    47. <p class="time">今日更新p>
    48. div>
    49. li>
    50. <li class="flex_c active">
    51. <img alt="" class="cover" data-src="{% static 'images/rank_list_5.png' %}" src="{% static 'images/rank_list_5.png' %}" lazy="loaded">
    52. <div class="item_info"><p class="name"><a href="{% url 'player:rank' %}?id=5">万物DJ榜a>p>
    53. <p class="time">今日更新p>
    54. div>
    55. li>
    56. <li class="flex_c active">
    57. <img alt="" class="cover" data-src="{% static 'images/rank_list_6.png' %}" src="{% static 'images/rank_list_6.png' %}" lazy="loaded">
    58. <div class="item_info"><p class="name"><a href="{% url 'player:rank' %}?id=6">会员畅听榜a>p>
    59. <p class="time">今日更新p>
    60. div>
    61. li>
    62. ul>
    63. div>
    64. div>
    65. <div class="con_r">
    66. <div>
    67. <div><span class="title">{{rank_name}}span> <span class="update_time">更新时间:{{updatetime}}span>div>
    68. <div class="btns">
    69. <button class="play bg_primary">
    70. <i class="glyphicon glyphicon-play">i> <span>立即播放span>
    71. button>
    72. <button><i class="glyphicon glyphicon-plus">i> <span>添加span>
    73. button>
    74. <button>
    75. <i class="glyphicon glyphicon-heart">i> <span>收藏span>
    76. button>
    77. div>
    78. <div class="list_out">
    79. <div class="list_head head_name_rank" style="">
    80. <ul class="flex_c">
    81. <li class="head_num">序号li>
    82. <li class="head_name">歌曲li>
    83. <li class="head_artist">歌手li>
    84. <li class="head_album">发布时间li>
    85. <li class="head_time">时长li>
    86. ul>
    87. div>
    88. <ul class="rank_list">
    89. {% for item in res %}
    90. <li class="song_item flex_c">
    91. <div class="song_rank flex_c">
    92. {% if page == 1 %}
    93. {% if forloop.counter == 1 %}
    94. <div class="rank_num top1">div>
    95. {% elif forloop.counter == 2 %}
    96. <div class="rank_num top2">div>
    97. {% elif forloop.counter == 3 %}
    98. <div class="rank_num top3">div>
    99. {% else %}
    100. <div class="rank_num"><span>{{forloop.counter}}span>div>
    101. {% endif %}
    102. {% else %}
    103. <div class="rank_num"><span>{{forloop.counter|add:page_num}}span>div>
    104. {% endif %}
    105. <div class="status">div>
    106. div>
    107. <div class="song_name flex_c">
    108. <a title="{{item.name}}" href="{% url 'player:album_song' %}?sid={{item.id}}" class="name">{{item.name}}a>
    109. div>
    110. <div class="song_artist">
    111. <span title="{{item.singler.name}}">{{item.singler.name}}span>
    112. div>
    113. <div class="song_album">
    114. <span>{{item.addtime}}span>
    115. div>
    116. <div class="song_time"><span>{{item.get_song_duration}}span>div>
    117. <div class="song_opts flex_c">
    118. <i class="glyphicon glyphicon-plus">i>
    119. <i class="glyphicon glyphicon-play">i>
    120. <i class="glyphicon glyphicon-heart">i>
    121. div>
    122. li>
    123. {% endfor %}
    124. ul>
    125. {% if list_num < 1 %}
    126. <div class="nodata flex_c">
    127. <div class="inner">
    128. <img src="{% static 'images/nodata.png' %}"
    129. alt="" class="nodata_img">
    130. <div class="tip"><p>暂无相关数据p>div>
    131. div>
    132. div>
    133. {% endif %}
    134. <div class="loading-mask" style="display: none;">
    135. <div class="loading-wrap">
    136. <div class="load"><span class="side1">span> <span
    137. class="side2">span> <span class="mid">span>
    138. <span class="side2">span> <span
    139. class="side1">span>div>
    140. div>
    141. div>
    142. div>
    143. {% if list_num > 0 %}
    144. <div class="page">
    145. <i class="li-page glyphicon glyphicon-menu-left notPointer">i>
    146. <ul>
    147. {% for index in res.paginator.page_range %}
    148. {% if res.number == index %}
    149. <li><a href="#" class="notCursor currentPage">{{index}}a>li>
    150. {% else %}
    151. <li><a href="{% url 'player:rank' %}?page={{index}}">{{index}}a>li>
    152. {% endif %}
    153. {% endfor %}
    154. ul>
    155. <i class="glyphicon glyphicon-menu-right li-page">i>
    156. div>
    157. {% endif %}
    158. div>
    159. div>
    160. div>
    161. {% endblock content %}

    设置跳转入口

    点击推荐排行榜中更多按钮,进入排行榜列表。

    内容如下:

    1. <div class="title">
    2. <div class="name">推荐排行榜div>
    3. <ul>
    4. <li><a href="{% url 'player:rank' %}">更多>a>li>
    5. ul>
    6. div>

    播放器功能开发

    其他功能大致算完成了,开始做播放音乐的功能开发。

    播放器设置在网站的底部,采用固定悬浮;

    可以左右切换音乐,设置音量,查看播放音乐列表。

    设置路由

    主要用来获取播放器音乐列表信息。

    1. # 播放器列表
    2. path('play_list', views.play_list, name='play_list'),

    模板页面

    播放器的主要功能采用之前使用html做的播放器,

    直接嵌入到django音乐网站基类模板(templates/common/base.html)的底部。

    内容如下:

    1. <div id="music_all">
    2. <div class="music_main" style="opacity:1;background:#fff">
    3. <div class="music_left">
    4. <img class="music_img" id="music_img" src="{% static 'images/s1.jpg' %}" alt="">
    5. div>
    6. <audio id="player">
    7. <source src="/media/uploads/1691649371/七里香_-_周杰伦.mp3" type="audio/mpeg">
    8. 您的浏览器不支持 audio 元素。
    9. audio>
    10. <div class="play_left">
    11. <div class="music_title">
    12. <span class="music_name">七里香 – 周杰伦span>
    13. <span class="totalTimeSpan">/04:59span>
    14. <span class="playTimeSpan">00:00span>
    15. div>
    16. <div class="music_rate">
    17. <div class="music-progress">div>
    18. div>
    19. div>
    20. <div class="play_right">
    21. <i class="glyphicon glyphicon-step-backward" id="music_prev">i>
    22. <i class="glyphicon glyphicon-play" id="music_dian">i>
    23. <i class="glyphicon glyphicon-step-forward" id="music_next">i>
    24. div>
    25. <div class="music_right">
    26. <ul>
    27. <li><i class="glyphicon glyphicon-th-list" id="setList">i>li>
    28. <li><i class="glyphicon glyphicon-volume-up" id="setVolume">i>li>
    29. <li>
    30. <div class="slider">
    31. <input type="range" id="volume" min="0" max="100" value="0">
    32. div>
    33. li>
    34. ul>
    35. div>
    36. div>
    37. <div class="songList" style="display:none">
    38. <div class="list_top flex_c">
    39. <div id="play_title">div>
    40. <div class="flex_c">
    41. <div class="clear_all"><i class="glyphicon glyphicon-trash">i> <span
    42. class="clear_btn">清空列表span>
    43. div>
    44. <i class="close glyphicon glyphicon-remove">i>div>
    45. div>
    46. <div class="list_con">
    47. <div id="play_list" style="transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
    48. transition-duration: 0ms; transform: translate(0px) scale(1) translateZ(0px);overflow-y: scroll;height:400px;">div>
    49. <div style="position: absolute; z-index: 9999; width: 7px; bottom: 2px; top: 2px; right: 1px; overflow: hidden;"
    50. class="bscroll-vertical-scrollbar">
    51. <div style="box-sizing: border-box; position: absolute; background: rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.9); border-radius: 3px; width: 100%; transition-duration: 0ms; height: 190px; transform: translateY(0px) translateZ(0px); transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);"
    52. class="bscroll-indicator">div>
    53. div>
    54. div>
    55. div>
    56. div>

    效果:

    脚本渲染

    使用JavaScript来实例化audio的提供的接口方法,并结合后台数据进行渲染和调用audio来实现播放器功能。

    列表渲染和播放器实现

    内容如下:

    1. window.onload = function () {
    2. // 绑定音频元素
    3. var $player = document.getElementById('player');
    4. // 绑定播放按钮
    5. var $dian = document.getElementById('music_dian');
    6. // 设置音频初始属性
    7. var volume_num = 0.5;
    8. // 当前歌曲索引
    9. var currentIndex = 0;
    10. // 设置播放列表
    11. var music_list = [{
    12. 'id': 1,
    13. 'cover': '/static/images/s1.jpg',
    14. 'singer': '周杰伦',
    15. 'song_name': '七里香',
    16. 'song_path': '/media/uploads/1691649371/七里香_-_周杰伦.mp3'
    17. },];
    18. // 设置播放器音乐列表
    19. var play_list = document.getElementById('play_list');
    20. var play_title = document.getElementById('play_title');
    21. // 初始化设置
    22. setInit();
    23. // 绑定音频控制开关
    24. $dian.onclick = function () {
    25. if (this.classList == 'glyphicon glyphicon-play') {
    26. this.className = 'glyphicon glyphicon-pause';
    27. $player.play();
    28. } else {
    29. // layui-icon-pause
    30. this.className = 'glyphicon glyphicon-play';
    31. $player.pause();
    32. }
    33. };
    34. // 设置播放器初始属性
    35. function setInit() {
    36. // 设定音量
    37. $player.volume = volume_num;
    38. $('#volume').val(volume_num * 100);
    39. // 通过同步方式获取播放列表信息
    40. $.ajaxSettings.async = false;
    41. $.getJSON('/play_list', {}, function (res) {
    42. // 赋值播放列表
    43. music_list = res.list;
    44. });
    45. // 设定歌曲封面
    46. $('#music_img').attr('src', '/media/' + music_list[0].cover);
    47. // 设定歌曲名称和歌手
    48. $('.music_name').text(music_list[0].song_name + ' - ' + music_list[0].singer);
    49. // 设定歌曲路径
    50. $player.src = '/media/' + music_list[0].song_path;
    51. // 设置播放器音乐列表
    52. set_media_list(music_list)
    53. }
    54. // 监听播放器播放时间改变事件
    55. $player.addEventListener('timeupdate', function () {
    56. // 当前播放时间
    57. var currentTime = $player.currentTime;
    58. // 总时间
    59. var totalTime = $player.duration;
    60. // 当是数字的时候
    61. if (!isNaN(totalTime)) {
    62. // 得到播放时间与总时长的比值
    63. var rate = currentTime / totalTime;
    64. // 设置时间显示
    65. // 播放时间
    66. $('.playTimeSpan').text(musicTime(currentTime));
    67. // 总时长
    68. $('.totalTimeSpan').text('/' + musicTime(totalTime));
    69. // 设置进度条
    70. $('.music-progress').css('width', rate * 441 + 'px');
    71. }
    72. });
    73. // 设置音量
    74. $('#volume').change(function () {
    75. volume_num = $(this).val();
    76. $player.volume = volume_num * 0.01
    77. });
    78. // 上一首
    79. $('#music_prev').click(function () {
    80. if (currentIndex > 0) {
    81. currentIndex -= 1;
    82. } else {
    83. // 切换到最后一首
    84. currentIndex = music_list.length - 1;
    85. }
    86. // 设置播放标识为暂停
    87. $dian.className = 'glyphicon glyphicon-play';
    88. // 播放时间
    89. $('.playTimeSpan').text('00:00');
    90. // 设置歌曲进度归零
    91. $('.music-progress').css('width', '1px');
    92. // 设置歌曲
    93. setMusic();
    94. });
    95. // 下一首
    96. $('#music_next').click(function () {
    97. if (currentIndex < (music_list.length - 1)) {
    98. currentIndex += 1;
    99. } else {
    100. // 切换为第一首
    101. currentIndex = 0;
    102. }
    103. // 设置播放标识为暂停
    104. $dian.className = 'glyphicon glyphicon-play';
    105. // 播放时间
    106. $('.playTimeSpan').text('00:00');
    107. // 设置歌曲进度归零
    108. $('.music-progress').css('width', '1px');
    109. // 设置歌曲
    110. setMusic();
    111. });
    112. // 设置播放器歌曲信息
    113. function setMusic() {
    114. // 设定歌曲封面
    115. $('#music_img').attr('src', '/media/' + music_list[currentIndex].cover);
    116. // 设定歌曲名称和歌手
    117. $('.music_name').text(music_list[currentIndex].song_name +
    118. ' - ' + music_list[currentIndex].singer);
    119. // 设定歌曲路径
    120. $player.src = '/media/' + music_list[currentIndex].song_path;
    121. }
    122. // 歌曲时长(00:00)
    123. function musicTime(sens) {
    124. // 分
    125. var m = parseInt(sens / 60);
    126. // 秒
    127. var s = parseInt(sens % 60);
    128. // 补零
    129. m = m > 9 ? m : "0" + m;
    130. s = s > 9 ? s : "0" + s;
    131. return m + ":" + s;
    132. }
    133. // 设置音乐播放器列表
    134. function set_media_list(music_list) {
    135. var play_html = '';
    136. for (var i = 0; i < music_list.length; i++) {
    137. if (i) {
    138. play_html += '
      ' +
    139. '
      ' + (i + 1) + '' +
    140. ';
    141. } else {
    142. play_html += '
      ' +
    143. '
      ' + (i + 1) + '' +
    144. '';
    145. }
    146. play_html += '' +
    147. '' +
    148. '' +
    149. '' +
    150. '
      ' +
  • '
    ' +
  • '
    ' +
  • '
    ' +
  • '
    ' +
  • '
    ' + music_list[i].duration + '
    '
    +
  • '
    ' +
  • '  ' +
  • '  ' +
  • '  ' +
  • '  ' +
  • '
    ' +
  • '
    ';
  • }
  • play_title.innerHTML='播放列表 (共'+ music_list.length +'首)';
  • play_list.innerHTML=play_html;
  • console.log(play_title);
  • }
  • };
  • 音乐播放器列表展示关闭

    可通过底部播放器列表icon来打开和关闭播放列表;

    也可以通过播放列表中关闭icon来隐藏播放列表。

    内容如下:

    1. $('#setList').click(function(){
    2. // 展示关闭音乐播放器列表
    3. var songList = $('.songList');
    4. if (songList.css('display') == 'none') {
    5. songList.show();
    6. }else{
    7. songList.hide();
    8. }
    9. })
    10. $('.close').click(function(){
    11. // 关闭播放列表
    12. $('.songList').hide();
    13. });

    总结

    本篇主要是推荐页-排行榜功能改为动态数据及播放器功能部分实现,可以播放音乐和左右切换以及查看播放音乐列表。

  • 相关阅读:
    常用百宝箱——日志处理
    Linux下ThinkPHP5实现定时器任务 - 结合crontab
    LeetCode:29. 两数相除
    java 显示c控制台程序窗口,Windows-如何清除C中的控制台屏幕?
    如何封装Dao_java培训
    Java 新手如何使用Spring MVC RestAPI的加密
    95后工程师上班哼小曲?那些愉快上班的打工人,到底怎么做到的?
    Java List 集合取 交集、并集、差集、补集 Java集合取交集、Java集合并集
    使用CMake构建一个简单的C++项目
    多级缓存自用
  • 原文地址:https://blog.csdn.net/json_ligege/article/details/133776371