Skip to content

Commit fc1c8d8

Browse files
authored
Merge pull request #2292 from Younglina/master
添加复制歌词功能
2 parents 481ba6b + 9d807d1 commit fc1c8d8

File tree

6 files changed

+76
-2
lines changed

6 files changed

+76
-2
lines changed

src/locale/lang/en.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ export default {
244244
minePlaylists: 'My Playlists',
245245
likedPlaylists: 'Liked Playlists',
246246
cardiacMode: 'Cardiac Mode',
247+
copyLyric: 'Copy Lyric',
248+
copyLyricWithTranslation: 'Copy Lyric With Translation',
247249
},
248250
toast: {
249251
savedToPlaylist: 'Saved to playlist',

src/locale/lang/tr.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ export default {
230230
minePlaylists: 'My Playlists',
231231
likedPlaylists: 'Liked Playlists',
232232
cardiacMode: 'Cardiac Mode',
233+
copyLyric: 'Copy Lyric',
234+
copyLyricWithTranslation: 'Copy Lyric With Translation',
233235
},
234236
toast: {
235237
savedToMyLikedSongs: 'Beğendiğim Müziklere Kaydet',

src/locale/lang/zh-CN.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ export default {
243243
minePlaylists: '创建的歌单',
244244
likedPlaylists: '收藏的歌单',
245245
cardiacMode: '心动模式',
246+
copyLyric: '复制歌词',
247+
copyLyricWithTranslation: '复制歌词(含翻译)',
246248
},
247249
toast: {
248250
savedToPlaylist: '已添加到歌单',

src/locale/lang/zh-TW.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ export default {
240240
minePlaylists: '我建立的歌單',
241241
likedPlaylists: '收藏的歌單',
242242
cardiacMode: '心動模式',
243+
copyLyric: '複製歌詞',
244+
copyLyricWithTranslation: '複製歌詞(含翻譯)',
243245
},
244246
toast: {
245247
savedToPlaylist: '已新增至歌單',

src/utils/lyrics.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,30 @@ function trimContent(content) {
8484
let t = content.trim();
8585
return t.length < 1 ? content : t;
8686
}
87+
88+
/**
89+
* @param {string} lyric
90+
*/
91+
export async function copyLyric(lyric) {
92+
const textToCopy = lyric;
93+
if (navigator.clipboard && navigator.clipboard.writeText) {
94+
try {
95+
await navigator.clipboard.writeText(textToCopy);
96+
} catch (err) {
97+
alert('复制失败,请手动复制!');
98+
}
99+
} else {
100+
const tempInput = document.createElement('textarea');
101+
tempInput.value = textToCopy;
102+
tempInput.style.position = 'absolute';
103+
tempInput.style.left = '-9999px';
104+
document.body.appendChild(tempInput);
105+
tempInput.select();
106+
try {
107+
document.execCommand('copy');
108+
} catch (err) {
109+
alert('复制失败,请手动复制!');
110+
}
111+
document.body.removeChild(tempInput);
112+
}
113+
}

src/views/lyrics.vue

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,18 +248,38 @@
248248
@dblclick="clickLyricLine(line.time, true)"
249249
>
250250
<div class="content">
251-
<span v-if="line.contents[0]">{{ line.contents[0] }}</span>
251+
<span
252+
v-if="line.contents[0]"
253+
@click.right="openLyricMenu($event, line, 0)"
254+
>{{ line.contents[0] }}</span
255+
>
252256
<br />
253257
<span
254258
v-if="
255259
line.contents[1] &&
256260
$store.state.settings.showLyricsTranslation
257261
"
258262
class="translation"
263+
@click.right="openLyricMenu($event, line, 1)"
259264
>{{ line.contents[1] }}</span
260265
>
261266
</div>
262267
</div>
268+
<ContextMenu v-if="!noLyric" ref="lyricMenu">
269+
<div class="item" @click="copyLyric(false)">{{
270+
$t('contextMenu.copyLyric')
271+
}}</div>
272+
<div
273+
v-if="
274+
rightClickLyric &&
275+
rightClickLyric.contents[1] &&
276+
$store.state.settings.showLyricsTranslation
277+
"
278+
class="item"
279+
@click="copyLyric(true)"
280+
>{{ $t('contextMenu.copyLyricWithTranslation') }}</div
281+
>
282+
</ContextMenu>
263283
</div>
264284
</transition>
265285
</div>
@@ -284,9 +304,10 @@
284304
285305
import { mapState, mapMutations, mapActions } from 'vuex';
286306
import VueSlider from 'vue-slider-component';
307+
import ContextMenu from '@/components/ContextMenu.vue';
287308
import { formatTrackTime } from '@/utils/common';
288309
import { getLyric } from '@/api/track';
289-
import { lyricParser } from '@/utils/lyrics';
310+
import { lyricParser, copyLyric } from '@/utils/lyrics';
290311
import ButtonIcon from '@/components/ButtonIcon.vue';
291312
import * as Vibrant from 'node-vibrant/dist/vibrant.worker.min.js';
292313
import Color from 'color';
@@ -299,6 +320,7 @@ export default {
299320
components: {
300321
VueSlider,
301322
ButtonIcon,
323+
ContextMenu,
302324
},
303325
data() {
304326
return {
@@ -312,6 +334,7 @@ export default {
312334
background: '',
313335
date: this.formatTime(new Date()),
314336
isFullscreen: !!document.fullscreenElement,
337+
rightClickLyric: null,
315338
};
316339
},
317340
computed: {
@@ -587,6 +610,21 @@ export default {
587610
this.player.play();
588611
}
589612
},
613+
openLyricMenu(e, lyric, idx) {
614+
this.rightClickLyric = { ...lyric, idx };
615+
this.$refs.lyricMenu.openMenu(e);
616+
e.preventDefault();
617+
},
618+
copyLyric(withTranslation) {
619+
if (this.rightClickLyric) {
620+
const idx = this.rightClickLyric.idx;
621+
if (!withTranslation) {
622+
copyLyric(this.rightClickLyric.contents[idx]);
623+
} else {
624+
copyLyric(this.rightClickLyric.contents.join(' '));
625+
}
626+
}
627+
},
590628
setLyricsInterval() {
591629
this.lyricsInterval = setInterval(() => {
592630
const progress = this.player.seek(null, false) ?? 0;
@@ -926,6 +964,7 @@ export default {
926964
transform-origin: center left;
927965
transform: scale(0.95);
928966
transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
967+
user-select: none;
929968
930969
span {
931970
opacity: 0.28;

0 commit comments

Comments
 (0)