playListInfo.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <script setup lang="ts">
  2. import {defineComponent, onBeforeMount, PropType, ref, watch} from "vue";
  3. import {MusicInfo, param_music_like, PlayList} from "@/types/musicType.ts";
  4. import LickIcon from "@/components/music/common/lickIcon.vue";
  5. import IconSvg from "@/components/public/icon/iconSvg.vue";
  6. import message from "@/components/public/kui/message";
  7. import {api_fetchMusic, api_likeMusic} from "@/apis/musicControl.ts";
  8. import {ErrorCode} from "@/types/apiTypes.ts";
  9. import {secondToTimeStr} from "../../../util/time.ts";
  10. import HideText from "@/components/public/hideText.vue";
  11. import {KuiDialogCmd} from "@/components/public/kui-dialog-cmd.ts";
  12. import addPlayList from "@/components/music/dialog/addPlayList.vue";
  13. defineComponent({name: "play-list-info"});
  14. const props = defineProps({
  15. playList: {
  16. type: Object as PropType<PlayList>,
  17. default: () => ({})
  18. },
  19. windowId: {
  20. type: String,
  21. default: ""
  22. }
  23. })
  24. const musicList = ref<MusicInfo[]>([])
  25. let playlist_id = ref(0)
  26. const lock_loading = ref(false);
  27. const scanCount = ref(0)
  28. const search_key = ref("");
  29. const search_page = ref(1);
  30. const page_limit = 10;
  31. const emits = defineEmits<{
  32. (e: 'reload'): void
  33. }>()
  34. async function loadPlayListMusic(playList: PlayList, page: number, key: string){
  35. if (lock_loading.value)
  36. {
  37. message.info("正在加载中,请稍后");
  38. return;
  39. }
  40. lock_loading.value = true;
  41. let res = await api_fetchMusic(playList.id, page, page_limit, key)
  42. lock_loading.value = false;
  43. if (res.code === ErrorCode.success)
  44. {
  45. if (page === 1)
  46. {
  47. scanCount.value = res.data.total?? 0;
  48. }
  49. let pageData = res.data.data;
  50. for ( let i = 0; i < pageData.length; i++)
  51. {
  52. pageData[i].isLike = !!pageData[i].isLike;
  53. pageData[i].isLocal = !!pageData[i].isLocal;
  54. musicList.value.push(pageData[i]);
  55. }
  56. } else {
  57. message.error(res.msg);
  58. }
  59. }
  60. onBeforeMount(()=>{
  61. if (props.playList && props.playList.id)
  62. {
  63. loadPlayListMusic(props.playList, search_page.value, search_key.value);
  64. }
  65. })
  66. watch(()=>props.playList, ()=>{
  67. if (playlist_id.value !== props.playList.id)
  68. {
  69. musicList.value = [];
  70. loadPlayListMusic(props.playList, 1, search_key.value);
  71. }
  72. })
  73. function playMusic(item: MusicInfo) {
  74. console.log(item);
  75. message.info(`play ${item.name}`);
  76. }
  77. async function likeMusic(item: MusicInfo) {
  78. // console.log(item);
  79. // message.info(`like ${item.name}`);
  80. let nextLike = !item.isLike;
  81. let param: param_music_like = {
  82. musicId: item.id,
  83. isLike: nextLike
  84. }
  85. let res = await api_likeMusic(param);
  86. if (res.code === ErrorCode.success)
  87. {
  88. item.isLike = nextLike;
  89. if ( props.playList.isLike )
  90. {
  91. // 移除该项
  92. musicList.value = musicList.value.filter(item => item.id !== param.musicId);
  93. }
  94. }
  95. else {
  96. message.error(res.msg);
  97. }
  98. }
  99. function showMore(item: MusicInfo) {
  100. console.log(item);
  101. message.info(`show ${item.name}`);
  102. }
  103. let load_more_repeat = 0;
  104. async function loadMore()
  105. {
  106. if (musicList.value.length >= scanCount.value)
  107. {
  108. if (load_more_repeat > 3)
  109. {
  110. message.info("没有更多数据了");
  111. load_more_repeat = 0;
  112. return;
  113. }
  114. return;
  115. }
  116. search_page.value++;
  117. message.info("加载剩余数据");
  118. await loadPlayListMusic(props.playList, search_page.value, search_key.value);
  119. }
  120. const playlist_dialog = new KuiDialogCmd({
  121. showContent: addPlayList,
  122. mountTarget: props.windowId,
  123. className: 'dialog',
  124. on: { },
  125. onClose: closeDialogHandle
  126. });
  127. function closeDialogHandle()
  128. {
  129. console.log("close dialog");
  130. emits('reload');
  131. }
  132. function editBtnClickHandle()
  133. {
  134. // message.info("edit btn click");
  135. playlist_dialog.show({
  136. playlist: props.playList
  137. })
  138. }
  139. function deleteBtnClickHandle()
  140. {
  141. message.info("delete btn click");
  142. }
  143. </script>
  144. <template>
  145. <div class="play-list-info">
  146. <div class="info">
  147. <div class="cover">
  148. <img :src="playList.cover" alt="">
  149. </div>
  150. <div class="info-content">
  151. <div class="name">{{playList.name}}</div>
  152. <div class="desc">{{playList.description}}</div>
  153. <div class="time">创建时间: {{playList.createTime}}</div>
  154. </div>
  155. <div
  156. v-if="!playList.isLike"
  157. class="edit-btn"
  158. @click="editBtnClickHandle">
  159. <icon-svg icon-name="edit"/>
  160. </div>
  161. <div v-if="!playList.isLike"
  162. class="delete-btn"
  163. @click="deleteBtnClickHandle"
  164. >
  165. <icon-svg icon-name="remove"/>
  166. </div>
  167. </div>
  168. <div class="music-lists">
  169. <div class="music-list-head">
  170. <div class="music-list-item">
  171. <div class="cover">
  172. </div>
  173. <div class="item-info">名称</div>
  174. <div class="origin">来源</div>
  175. <div class="duration">时长</div>
  176. <div class="isLike">喜欢</div>
  177. </div>
  178. </div>
  179. <div class="music-list-con scroll" @scrollend="loadMore()">
  180. <div v-for="item in musicList"
  181. class="music-list-item"
  182. @click="playMusic(item)"
  183. >
  184. <div class="cover">
  185. <img :src="item.cover" alt=""/>
  186. </div>
  187. <hide-text class="item-info" :text="item.name"
  188. :enable-copy="true"
  189. :copy-success="()=>{message.success('复制歌曲名称成功')}">
  190. <div class="name">{{item.name}}</div>
  191. <div class="artists">{{item.artists}}</div>
  192. </hide-text>
  193. <div class="origin">{{item.origin}}</div>
  194. <div class="duration">{{ secondToTimeStr(item.duration, "m分s秒" )}}</div>
  195. <lick-icon class="isLike" :like="item.isLike"
  196. @click.stop.capture="likeMusic(item)"/>
  197. <div class="more">
  198. <icon-svg icon-name="more" @click.stop.capture="showMore(item)"></icon-svg>
  199. </div>
  200. </div>
  201. </div>
  202. </div>
  203. </div>
  204. </template>
  205. <style scoped>
  206. @import "../../../assets/public.css";
  207. .play-list-info {
  208. display: flex;
  209. flex-direction: column;
  210. width: 100%;
  211. height: 100%;
  212. }
  213. .info {
  214. display: flex;
  215. width: 100%;
  216. height: 160px;
  217. padding: 10px;
  218. box-sizing: border-box;
  219. }
  220. .info .cover {
  221. width: 130px;
  222. height: 130px;
  223. background-color: #ccc;
  224. overflow: hidden;
  225. margin-left: 15px;
  226. margin-top: 5px;
  227. }
  228. .info-content {
  229. width: calc(100% - 130px);
  230. height: 100%;
  231. padding-left: 30px;
  232. font-size: 1em;
  233. }
  234. .info-content .name {
  235. font-size: 1.6rem;
  236. font-weight: bold;
  237. }
  238. .info-content .desc {
  239. font-size: 1rem;
  240. color: var(--color-text-subtitle);
  241. }
  242. .info-content .time {
  243. font-size: 0.8rem;
  244. margin-top: 10px;
  245. color: #999;
  246. }
  247. .edit-btn {
  248. position: absolute;
  249. top: 10px;
  250. right: 10px;
  251. width: 30px;
  252. height: 30px;
  253. border-radius: 50%;
  254. background-color: var(--color-background);
  255. display: flex;
  256. justify-content: center;
  257. align-items: center;
  258. cursor: pointer;
  259. }
  260. .delete-btn {
  261. position: absolute;
  262. top: 10px;
  263. right: 50px;
  264. width: 30px;
  265. height: 30px;
  266. border-radius: 50%;
  267. background-color: var(--color-background);
  268. color: var(--color-text-money);
  269. display: flex;
  270. justify-content: center;
  271. align-items: center;
  272. cursor: pointer;
  273. }
  274. </style>