v2.0.0
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
<template>
|
||||
<div :class="$style.list">
|
||||
<div class="thead">
|
||||
<table>
|
||||
<thead>
|
||||
<tr v-if="actionButtonsVisible">
|
||||
<th class="num" style="width: 5%;">#</th>
|
||||
<th class="nobreak">{{ $t('music_name') }}</th>
|
||||
<th class="nobreak" style="width: 22%;">{{ $t('music_singer') }}</th>
|
||||
<th class="nobreak" style="width: 22%;">{{ $t('music_album') }}</th>
|
||||
<th class="nobreak" style="width: 9%;">{{ $t('music_time') }}</th>
|
||||
<th class="nobreak" style="width: 16%;">{{ $t('action') }}</th>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<th class="num" style="width: 5%;">#</th>
|
||||
<th class="nobreak">{{ $t('music_name') }}</th>
|
||||
<th class="nobreak" style="width: 25%;">{{ $t('music_singer') }}</th>
|
||||
<th class="nobreak" style="width: 28%;">{{ $t('music_album') }}</th>
|
||||
<th class="nobreak" style="width: 10%;">{{ $t('music_time') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div v-show="list.length" ref="dom_listContent" :class="$style.content">
|
||||
<base-virtualized-list
|
||||
v-if="actionButtonsVisible" ref="listRef" v-slot="{ item, index }" :list="list" key-name="id"
|
||||
:item-height="listItemHeight" container-class="scroll" content-class="list"
|
||||
@scroll="saveListPosition" @contextmenu.capture="handleListRightClick"
|
||||
>
|
||||
<div
|
||||
class="list-item" :class="[{ [$style.active]: playerInfo.isPlayList && playerInfo.playIndex === index }, { selected: selectedIndex == index || rightClickSelectedIndex == index }, { active: selectedList.includes(item) }, { disabled: !assertApiSupport(item.source) }]"
|
||||
@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)"
|
||||
>
|
||||
<div class="list-item-cell no-select num" style="flex: 0 0 5%;" @click.stop>{{ index + 1 }}</div>
|
||||
<div class="list-item-cell auto name" :aria-label="item.name">
|
||||
<span class="select name">{{ item.name }}</span>
|
||||
<span v-if="isShowSource" class="no-select label-source">{{ item.source }}</span>
|
||||
</div>
|
||||
<div class="list-item-cell" style="flex: 0 0 22%;"><span class="select" :aria-label="item.singer">{{ item.singer }}</span></div>
|
||||
<div class="list-item-cell" style="flex: 0 0 22%;"><span class="select" :aria-label="item.meta.albumName">{{ item.meta.albumName }}</span></div>
|
||||
<div class="list-item-cell" style="flex: 0 0 9%;"><span class="no-select">{{ item.interval || '--/--' }}</span></div>
|
||||
<div class="list-item-cell" style="flex: 0 0 16%; padding-left: 0; padding-right: 0;">
|
||||
<material-list-buttons :index="index" :download-btn="assertApiSupport(item.source) && item.source != 'local'" @btn-click="handleListBtnClick" />
|
||||
</div>
|
||||
</div>
|
||||
</base-virtualized-list>
|
||||
<base-virtualized-list
|
||||
v-else ref="listRef" v-slot="{ item, index }" :list="list" key-name="id"
|
||||
:item-height="listItemHeight" container-class="scroll" content-class="list"
|
||||
@scroll="saveListPosition" @contextmenu.capture="handleListRightClick"
|
||||
>
|
||||
<div
|
||||
class="list-item"
|
||||
:class="[{ [$style.active]: playerInfo.isPlayList && playerInfo.playIndex === index }, { selected: selectedIndex == index || rightClickSelectedIndex == index }, { active: selectedList.includes(item) }, { disabled: !assertApiSupport(item.source) }]"
|
||||
@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)"
|
||||
>
|
||||
<div class="list-item-cell no-select num" style="flex: 0 0 5%;" @click.stop>{{ index + 1 }}</div>
|
||||
<div class="list-item-cell auto name">
|
||||
<span class="select name" :aria-label="item.name">{{ item.name }}</span>
|
||||
<span v-if="isShowSource" class="no-select label-source">{{ item.source }}</span>
|
||||
</div>
|
||||
<div class="list-item-cell" style="flex: 0 0 25%;"><span class="select" :aria-label="item.singer">{{ item.singer }}</span></div>
|
||||
<div class="list-item-cell" style="flex: 0 0 28%;"><span class="select" :aria-label="item.meta.albumName">{{ item.meta.albumName }}</span></div>
|
||||
<div class="list-item-cell" style="flex: 0 0 10%;"><span class="no-select">{{ item.interval || '--/--' }}</span></div>
|
||||
</div>
|
||||
</base-virtualized-list>
|
||||
</div>
|
||||
<div v-show="!list.length" :class="$style.noItem">
|
||||
<p v-text="$t('no_item')" />
|
||||
</div>
|
||||
<common-list-add-modal
|
||||
v-model:show="isShowListAdd" :is-move="isMove" :from-list-id="listId"
|
||||
:music-info="selectedAddMusicInfo" :exclude-list-id="excludeListIds" teleport="#view"
|
||||
/>
|
||||
<common-list-add-multiple-modal
|
||||
v-model:show="isShowListAddMultiple" :from-list-id="listId"
|
||||
:is-move="isMoveMultiple" :music-list="selectedList" :exclude-list-id="excludeListIds" teleport="#view" @confirm="removeAllSelect"
|
||||
/>
|
||||
<common-download-modal v-model:show="isShowDownload" :music-info="selectedDownloadMusicInfo" teleport="#view" />
|
||||
<common-download-multiple-modal v-model:show="isShowDownloadMultiple" :list="selectedList" teleport="#view" @confirm="removeAllSelect" />
|
||||
<search-list :list="list" :visible="isShowSearchBar" @action="handleMusicSearchAction" />
|
||||
<music-sort-modal v-model:show="isShowMusicSortModal" :music-info="selectedSortMusicInfo" :selected-num="selectedNum" @confirm="sortMusic" />
|
||||
<base-menu v-model="isShowItemMenu" :menus="menus" :xy="menuLocation" item-name="name" @menu-click="handleMenuClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { clipboardWriteText } from '@common/utils/electron'
|
||||
import { assertApiSupport } from '@renderer/store/utils'
|
||||
import SearchList from './components/SearchList'
|
||||
import MusicSortModal from './components/MusicSortModal'
|
||||
import useListInfo from './useListInfo'
|
||||
import useList from './useList'
|
||||
import useMenu from './useMenu'
|
||||
import usePlay from './usePlay'
|
||||
import useMusicDownload from './useMusicDownload'
|
||||
import useMusicAdd from './useMusicAdd'
|
||||
import useSort from './useSort'
|
||||
import useMusicActions from './useMusicActions'
|
||||
import useSearch from './useSearch'
|
||||
import useListScroll from './useListScroll'
|
||||
import { appSetting } from '@renderer/store/setting'
|
||||
export default {
|
||||
name: 'MusicList',
|
||||
components: {
|
||||
SearchList,
|
||||
MusicSortModal,
|
||||
},
|
||||
props: {
|
||||
listId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['show-menu'],
|
||||
setup(props, { emit }) {
|
||||
const actionButtonsVisible = appSetting['list.actionButtonsVisible']
|
||||
|
||||
let scrollIndex = null
|
||||
let isAnimation = false
|
||||
const handleRestoreScroll = (_scrollIndex, _isAnimation) => {
|
||||
scrollIndex = _scrollIndex
|
||||
isAnimation = _isAnimation
|
||||
if (isAnimation) restoreScroll(scrollIndex, isAnimation)
|
||||
console.log('handleRestoreScroll', scrollIndex, isAnimation)
|
||||
}
|
||||
const onLoadedList = () => {
|
||||
console.log('restoreScroll', scrollIndex, isAnimation)
|
||||
restoreScroll(scrollIndex, isAnimation)
|
||||
}
|
||||
|
||||
const {
|
||||
rightClickSelectedIndex,
|
||||
selectedIndex,
|
||||
dom_listContent,
|
||||
listRef,
|
||||
list,
|
||||
playerInfo,
|
||||
setSelectedIndex,
|
||||
isShowSource,
|
||||
excludeListIds,
|
||||
} = useListInfo({ props, onLoadedList })
|
||||
|
||||
const {
|
||||
selectedList,
|
||||
listItemHeight,
|
||||
handleSelectData,
|
||||
removeAllSelect,
|
||||
} = useList({ list })
|
||||
|
||||
const {
|
||||
handlePlayMusic,
|
||||
handlePlayMusicLater,
|
||||
doubleClickPlay,
|
||||
} = usePlay({ props, selectedList, list, removeAllSelect })
|
||||
|
||||
const {
|
||||
isShowListAdd,
|
||||
isMove,
|
||||
isShowListAddMultiple,
|
||||
isMoveMultiple,
|
||||
selectedAddMusicInfo,
|
||||
handleShowMusicAddModal,
|
||||
handleShowMusicMoveModal,
|
||||
} = useMusicAdd({ selectedList, list })
|
||||
|
||||
const {
|
||||
isShowDownload,
|
||||
isShowDownloadMultiple,
|
||||
selectedDownloadMusicInfo,
|
||||
handleShowDownloadModal,
|
||||
} = useMusicDownload({ selectedList, list })
|
||||
|
||||
const {
|
||||
isShowMusicSortModal,
|
||||
selectedNum,
|
||||
selectedSortMusicInfo,
|
||||
handleShowSortModal,
|
||||
sortMusic,
|
||||
} = useSort({ props, list, selectedList, removeAllSelect })
|
||||
|
||||
const {
|
||||
handleSearch,
|
||||
handleOpenMusicDetail,
|
||||
handleCopyName,
|
||||
handleRemoveMusic,
|
||||
} = useMusicActions({ props, list, removeAllSelect, selectedList })
|
||||
|
||||
const {
|
||||
menus,
|
||||
menuLocation,
|
||||
isShowItemMenu,
|
||||
showMenu,
|
||||
menuClick,
|
||||
} = useMenu({
|
||||
assertApiSupport,
|
||||
emit,
|
||||
|
||||
handleShowDownloadModal,
|
||||
handlePlayMusic,
|
||||
handlePlayMusicLater,
|
||||
handleSearch,
|
||||
handleShowMusicAddModal,
|
||||
handleShowMusicMoveModal,
|
||||
handleShowSortModal,
|
||||
handleOpenMusicDetail,
|
||||
handleCopyName,
|
||||
handleRemoveMusic,
|
||||
})
|
||||
|
||||
const {
|
||||
isShowSearchBar,
|
||||
searchList,
|
||||
handleMusicSearchAction,
|
||||
} = useSearch({
|
||||
setSelectedIndex,
|
||||
handlePlayMusic,
|
||||
listRef,
|
||||
})
|
||||
|
||||
const { saveListPosition, restoreScroll } = useListScroll({ props, listRef, list, handleRestoreScroll })
|
||||
|
||||
|
||||
const handleListItemClick = (event, index) => {
|
||||
if (rightClickSelectedIndex.value > -1) return
|
||||
handleSelectData(index)
|
||||
doubleClickPlay(index)
|
||||
}
|
||||
const handleListItemRightClick = (event, index) => {
|
||||
rightClickSelectedIndex.value = index
|
||||
showMenu(event, list.value[index], index)
|
||||
}
|
||||
const handleMenuClick = (action) => {
|
||||
let index = rightClickSelectedIndex.value
|
||||
rightClickSelectedIndex.value = -1
|
||||
menuClick(action, index)
|
||||
}
|
||||
const handleListRightClick = (event) => {
|
||||
if (!event.target.classList.contains('select')) return
|
||||
event.stopImmediatePropagation()
|
||||
let classList = dom_listContent.value.classList
|
||||
classList.add('copying')
|
||||
window.requestAnimationFrame(() => {
|
||||
let str = window.getSelection().toString()
|
||||
classList.remove('copying')
|
||||
str = str.split(/\n\n/).map(s => s.replace(/\n/g, ' ')).join('\n').trim()
|
||||
if (!str.length) return
|
||||
clipboardWriteText(str)
|
||||
})
|
||||
}
|
||||
const handleListBtnClick = ({ action, index }) => {
|
||||
switch (action) {
|
||||
case 'download':
|
||||
handleShowDownloadModal(index, true)
|
||||
break
|
||||
case 'play':
|
||||
handlePlayMusic(index, true)
|
||||
break
|
||||
case 'search':
|
||||
handleSearch(index)
|
||||
break
|
||||
case 'listAdd':
|
||||
handleShowMusicAddModal(index, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
const scrollToTop = () => {
|
||||
listRef.value.scrollTo(0, true)
|
||||
}
|
||||
|
||||
return {
|
||||
listItemHeight,
|
||||
handleListItemClick,
|
||||
selectedList,
|
||||
handleListItemRightClick,
|
||||
removeAllSelect,
|
||||
handleListBtnClick,
|
||||
rightClickSelectedIndex,
|
||||
selectedIndex,
|
||||
dom_listContent,
|
||||
listRef,
|
||||
excludeListIds,
|
||||
|
||||
menus,
|
||||
isShowItemMenu,
|
||||
menuLocation,
|
||||
handleMenuClick,
|
||||
|
||||
handleListRightClick,
|
||||
assertApiSupport,
|
||||
|
||||
isShowListAdd,
|
||||
isMove,
|
||||
isShowListAddMultiple,
|
||||
isMoveMultiple,
|
||||
selectedAddMusicInfo,
|
||||
|
||||
isShowMusicSortModal,
|
||||
selectedNum,
|
||||
selectedSortMusicInfo,
|
||||
sortMusic,
|
||||
|
||||
isShowDownload,
|
||||
isShowDownloadMultiple,
|
||||
selectedDownloadMusicInfo,
|
||||
|
||||
scrollToTop,
|
||||
|
||||
isShowSearchBar,
|
||||
searchList,
|
||||
handleMusicSearchAction,
|
||||
|
||||
list,
|
||||
playerInfo,
|
||||
|
||||
saveListPosition,
|
||||
isShowSource,
|
||||
handleRestoreScroll,
|
||||
|
||||
actionButtonsVisible,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.list {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
flex: auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
:global(.list-item) {
|
||||
&.active {
|
||||
color: var(--color-button-font);
|
||||
}
|
||||
}
|
||||
:global {
|
||||
.label-source {
|
||||
color: var(--color-primary);
|
||||
padding: 5px;
|
||||
font-size: .8em;
|
||||
line-height: 1;
|
||||
opacity: .75;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
min-height: 0;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.no-item {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
font-size: 24px;
|
||||
color: var(--color-font-label);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user