feat: 复制歌词

This commit is contained in:
Younglina 2024-08-27 13:21:01 +08:00
parent 481ba6bce3
commit 9d807d1d63
6 changed files with 76 additions and 2 deletions

View File

@ -244,6 +244,8 @@ export default {
minePlaylists: 'My Playlists',
likedPlaylists: 'Liked Playlists',
cardiacMode: 'Cardiac Mode',
copyLyric: 'Copy Lyric',
copyLyricWithTranslation: 'Copy Lyric With Translation',
},
toast: {
savedToPlaylist: 'Saved to playlist',

View File

@ -230,6 +230,8 @@ export default {
minePlaylists: 'My Playlists',
likedPlaylists: 'Liked Playlists',
cardiacMode: 'Cardiac Mode',
copyLyric: 'Copy Lyric',
copyLyricWithTranslation: 'Copy Lyric With Translation',
},
toast: {
savedToMyLikedSongs: 'Beğendiğim Müziklere Kaydet',

View File

@ -243,6 +243,8 @@ export default {
minePlaylists: '创建的歌单',
likedPlaylists: '收藏的歌单',
cardiacMode: '心动模式',
copyLyric: '复制歌词',
copyLyricWithTranslation: '复制歌词(含翻译)',
},
toast: {
savedToPlaylist: '已添加到歌单',

View File

@ -240,6 +240,8 @@ export default {
minePlaylists: '我建立的歌單',
likedPlaylists: '收藏的歌單',
cardiacMode: '心動模式',
copyLyric: '複製歌詞',
copyLyricWithTranslation: '複製歌詞(含翻譯)',
},
toast: {
savedToPlaylist: '已新增至歌單',

View File

@ -84,3 +84,30 @@ function trimContent(content) {
let t = content.trim();
return t.length < 1 ? content : t;
}
/**
* @param {string} lyric
*/
export async function copyLyric(lyric) {
const textToCopy = lyric;
if (navigator.clipboard && navigator.clipboard.writeText) {
try {
await navigator.clipboard.writeText(textToCopy);
} catch (err) {
alert('复制失败,请手动复制!');
}
} else {
const tempInput = document.createElement('textarea');
tempInput.value = textToCopy;
tempInput.style.position = 'absolute';
tempInput.style.left = '-9999px';
document.body.appendChild(tempInput);
tempInput.select();
try {
document.execCommand('copy');
} catch (err) {
alert('复制失败,请手动复制!');
}
document.body.removeChild(tempInput);
}
}

View File

@ -248,7 +248,11 @@
@dblclick="clickLyricLine(line.time, true)"
>
<div class="content">
<span v-if="line.contents[0]">{{ line.contents[0] }}</span>
<span
v-if="line.contents[0]"
@click.right="openLyricMenu($event, line, 0)"
>{{ line.contents[0] }}</span
>
<br />
<span
v-if="
@ -256,10 +260,26 @@
$store.state.settings.showLyricsTranslation
"
class="translation"
@click.right="openLyricMenu($event, line, 1)"
>{{ line.contents[1] }}</span
>
</div>
</div>
<ContextMenu v-if="!noLyric" ref="lyricMenu">
<div class="item" @click="copyLyric(false)">{{
$t('contextMenu.copyLyric')
}}</div>
<div
v-if="
rightClickLyric &&
rightClickLyric.contents[1] &&
$store.state.settings.showLyricsTranslation
"
class="item"
@click="copyLyric(true)"
>{{ $t('contextMenu.copyLyricWithTranslation') }}</div
>
</ContextMenu>
</div>
</transition>
</div>
@ -284,9 +304,10 @@
import { mapState, mapMutations, mapActions } from 'vuex';
import VueSlider from 'vue-slider-component';
import ContextMenu from '@/components/ContextMenu.vue';
import { formatTrackTime } from '@/utils/common';
import { getLyric } from '@/api/track';
import { lyricParser } from '@/utils/lyrics';
import { lyricParser, copyLyric } from '@/utils/lyrics';
import ButtonIcon from '@/components/ButtonIcon.vue';
import * as Vibrant from 'node-vibrant/dist/vibrant.worker.min.js';
import Color from 'color';
@ -299,6 +320,7 @@ export default {
components: {
VueSlider,
ButtonIcon,
ContextMenu,
},
data() {
return {
@ -312,6 +334,7 @@ export default {
background: '',
date: this.formatTime(new Date()),
isFullscreen: !!document.fullscreenElement,
rightClickLyric: null,
};
},
computed: {
@ -587,6 +610,21 @@ export default {
this.player.play();
}
},
openLyricMenu(e, lyric, idx) {
this.rightClickLyric = { ...lyric, idx };
this.$refs.lyricMenu.openMenu(e);
e.preventDefault();
},
copyLyric(withTranslation) {
if (this.rightClickLyric) {
const idx = this.rightClickLyric.idx;
if (!withTranslation) {
copyLyric(this.rightClickLyric.contents[idx]);
} else {
copyLyric(this.rightClickLyric.contents.join(' '));
}
}
},
setLyricsInterval() {
this.lyricsInterval = setInterval(() => {
const progress = this.player.seek(null, false) ?? 0;
@ -926,6 +964,7 @@ export default {
transform-origin: center left;
transform: scale(0.95);
transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
user-select: none;
span {
opacity: 0.28;