the music player is ready

This commit is contained in:
RockYang 2024-07-18 18:34:11 +08:00
parent b248560ba2
commit 7463cfc66c
14 changed files with 671 additions and 30 deletions

187
web/package-lock.json generated
View File

@ -26,6 +26,7 @@
"markmap-toolbar": "^0.17.0",
"markmap-view": "^0.16.0",
"md-editor-v3": "^2.2.1",
"memfs": "^4.9.3",
"mitt": "^3.0.1",
"pinia": "^2.1.4",
"qrcode": "^1.5.3",
@ -1917,6 +1918,57 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@jsonjoy.com/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/@jsonjoy.com/json-pack": {
"version": "1.0.4",
"resolved": "https://registry.npmmirror.com/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz",
"integrity": "sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==",
"dependencies": {
"@jsonjoy.com/base64": "^1.1.1",
"@jsonjoy.com/util": "^1.1.2",
"hyperdyperid": "^1.2.0",
"thingies": "^1.20.0"
},
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/@jsonjoy.com/util": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/@jsonjoy.com/util/-/util-1.2.0.tgz",
"integrity": "sha512-4B8B+3vFsY4eo33DMKyJPlQ3sBMpPFUZK2dr3O3rXrOGKKbYG44J0XSFkDo1VOQiri5HFEhIeVvItjR2xcazmg==",
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz",
@ -6965,9 +7017,9 @@
}
},
"node_modules/fs-monkey": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.3.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.6.tgz",
"integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
"dev": true
},
"node_modules/fs.realpath": {
@ -7381,6 +7433,14 @@
"node": ">=10.17.0"
}
},
"node_modules/hyperdyperid": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
"integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==",
"engines": {
"node": ">=10.18"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -8480,15 +8540,21 @@
}
},
"node_modules/memfs": {
"version": "3.4.1",
"resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.4.1.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
"dev": true,
"version": "4.9.3",
"resolved": "https://registry.npmmirror.com/memfs/-/memfs-4.9.3.tgz",
"integrity": "sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==",
"dependencies": {
"fs-monkey": "1.0.3"
"@jsonjoy.com/json-pack": "^1.0.3",
"@jsonjoy.com/util": "^1.1.2",
"tree-dump": "^1.0.1",
"tslib": "^2.0.0"
},
"engines": {
"node": ">= 4.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
}
},
"node_modules/memoize-one": {
@ -11394,6 +11460,17 @@
"node": ">=0.8"
}
},
"node_modules/thingies": {
"version": "1.21.0",
"resolved": "https://registry.npmmirror.com/thingies/-/thingies-1.21.0.tgz",
"integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==",
"engines": {
"node": ">=10.18"
},
"peerDependencies": {
"tslib": "^2"
}
},
"node_modules/thread-loader": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-3.0.4.tgz",
@ -11501,6 +11578,21 @@
"resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tree-dump": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/tree-dump/-/tree-dump-1.0.2.tgz",
"integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==",
"engines": {
"node": ">=10.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/streamich"
},
"peerDependencies": {
"tslib": "2"
}
},
"node_modules/tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz",
@ -12225,6 +12317,18 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"node_modules/webpack-dev-middleware/node_modules/memfs": {
"version": "3.5.3",
"resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"dev": true,
"dependencies": {
"fs-monkey": "^1.0.4"
},
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/webpack-dev-middleware/node_modules/schema-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.0.0.tgz",
@ -14028,6 +14132,29 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"@jsonjoy.com/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==",
"requires": {}
},
"@jsonjoy.com/json-pack": {
"version": "1.0.4",
"resolved": "https://registry.npmmirror.com/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz",
"integrity": "sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==",
"requires": {
"@jsonjoy.com/base64": "^1.1.1",
"@jsonjoy.com/util": "^1.1.2",
"hyperdyperid": "^1.2.0",
"thingies": "^1.20.0"
}
},
"@jsonjoy.com/util": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/@jsonjoy.com/util/-/util-1.2.0.tgz",
"integrity": "sha512-4B8B+3vFsY4eo33DMKyJPlQ3sBMpPFUZK2dr3O3rXrOGKKbYG44J0XSFkDo1VOQiri5HFEhIeVvItjR2xcazmg==",
"requires": {}
},
"@leichtgewicht/ip-codec": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz",
@ -18175,9 +18302,9 @@
}
},
"fs-monkey": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.3.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.6.tgz",
"integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
"dev": true
},
"fs.realpath": {
@ -18512,6 +18639,11 @@
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
"dev": true
},
"hyperdyperid": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz",
"integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -19387,12 +19519,14 @@
"dev": true
},
"memfs": {
"version": "3.4.1",
"resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.4.1.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
"dev": true,
"version": "4.9.3",
"resolved": "https://registry.npmmirror.com/memfs/-/memfs-4.9.3.tgz",
"integrity": "sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==",
"requires": {
"fs-monkey": "1.0.3"
"@jsonjoy.com/json-pack": "^1.0.3",
"@jsonjoy.com/util": "^1.1.2",
"tree-dump": "^1.0.1",
"tslib": "^2.0.0"
}
},
"memoize-one": {
@ -21633,6 +21767,12 @@
"thenify": ">= 3.1.0 < 4"
}
},
"thingies": {
"version": "1.21.0",
"resolved": "https://registry.npmmirror.com/thingies/-/thingies-1.21.0.tgz",
"integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==",
"requires": {}
},
"thread-loader": {
"version": "3.0.4",
"resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-3.0.4.tgz",
@ -21718,6 +21858,12 @@
"resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tree-dump": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/tree-dump/-/tree-dump-1.0.2.tgz",
"integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==",
"requires": {}
},
"tslib": {
"version": "2.4.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz",
@ -22301,6 +22447,15 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
"memfs": {
"version": "3.5.3",
"resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"dev": true,
"requires": {
"fs-monkey": "^1.0.4"
}
},
"schema-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.0.0.tgz",

View File

@ -26,6 +26,7 @@
"markmap-toolbar": "^0.17.0",
"markmap-view": "^0.16.0",
"md-editor-v3": "^2.2.1",
"memfs": "^4.9.3",
"mitt": "^3.0.1",
"pinia": "^2.1.4",
"qrcode": "^1.5.3",

BIN
web/public/files/test.mp3 Normal file

Binary file not shown.

View File

@ -0,0 +1,3 @@
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 6L0 0V12L10 6Z" fill="#FAF7F5"/>
</svg>

After

Width:  |  Height:  |  Size: 149 B

View File

@ -101,12 +101,35 @@
bottom 0
right 0
background-color rgba(14,8,8,.7)
padding 0 2px
padding 0 3px
font-family 'Input Sans'
font-size 14px
font-weight 700
border-radius .125rem
}
.play {
position absolute
width: 56px;
height 100%
top: 0;
left: 50%;
border none
border-radius 5px
background rgba(100, 100, 100, 0.3)
cursor pointer
color #ffffff
opacity 0
transform: translate(-50%, 0px);
transition opacity 0.3s ease 0s
}
&:hover {
.play {
opacity 1
//display block
}
}
}
}
@ -125,6 +148,13 @@
font-size 16px
font-weight 700
a {
color rgb(250 247 245)
&:hover {
text-decoration underline
}
}
.model {
color #E2E8F0
background-color #1C1616
@ -145,7 +175,7 @@
}
.right {
width 200px;
min-width 320px;
font-size 14px
padding 0 15px
@ -157,6 +187,7 @@
height 90px
.btn {
margin-right 10px
background-color #363030
border none
border-radius 5px
@ -167,6 +198,25 @@
background-color #5F5958
}
}
.btn-publish {
padding 2px 10px
.text {
margin-right 10px
}
}
.btn-icon {
background none
padding 6px
transition background 0.6s ease 0s
color #726E6C
&:hover {
background #3C3737
}
}
}
}
}
@ -175,6 +225,14 @@
background-color #2A2525
}
}
.music-player {
width 100%
position: fixed;
bottom: 0;
left: 0;
padding 20px 0
}
}
}

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1715987806624') format('woff2'),
url('iconfont.woff?t=1715987806624') format('woff'),
url('iconfont.ttf?t=1715987806624') format('truetype');
src: url('iconfont.woff2?t=1721292490257') format('woff2'),
url('iconfont.woff?t=1721292490257') format('woff'),
url('iconfont.ttf?t=1721292490257') format('truetype');
}
.iconfont {
@ -13,6 +13,54 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-pause:before {
content: "\e693";
}
.icon-prev:before {
content: "\e6a5";
}
.icon-next:before {
content: "\e6a7";
}
.icon-play:before {
content: "\e6a8";
}
.icon-remove:before {
content: "\e82b";
}
.icon-edit:before {
content: "\e61d";
}
.icon-download:before {
content: "\e83a";
}
.icon-more-vertical:before {
content: "\e8cb";
}
.icon-share1:before {
content: "\e661";
}
.icon-suno:before {
content: "\e608";
}
.icon-mp:before {
content: "\e6c4";
}
.icon-mp1:before {
content: "\e647";
}
.icon-control-simple:before {
content: "\e624";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,90 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "7156146",
"name": "暂停",
"font_class": "pause",
"unicode": "e693",
"unicode_decimal": 59027
},
{
"icon_id": "14929909",
"name": "多媒体控件Multimedia Controls (12)",
"font_class": "prev",
"unicode": "e6a5",
"unicode_decimal": 59045
},
{
"icon_id": "14929910",
"name": "多媒体控件Multimedia Controls (11)",
"font_class": "next",
"unicode": "e6a7",
"unicode_decimal": 59047
},
{
"icon_id": "14929913",
"name": "多媒体控件Multimedia Controls (13)",
"font_class": "play",
"unicode": "e6a8",
"unicode_decimal": 59048
},
{
"icon_id": "401063",
"name": "remove",
"font_class": "remove",
"unicode": "e82b",
"unicode_decimal": 59435
},
{
"icon_id": "968465",
"name": "编辑",
"font_class": "edit",
"unicode": "e61d",
"unicode_decimal": 58909
},
{
"icon_id": "6151351",
"name": "download",
"font_class": "download",
"unicode": "e83a",
"unicode_decimal": 59450
},
{
"icon_id": "18986714",
"name": "more",
"font_class": "more-vertical",
"unicode": "e8cb",
"unicode_decimal": 59595
},
{
"icon_id": "11903724",
"name": "share",
"font_class": "share1",
"unicode": "e661",
"unicode_decimal": 58977
},
{
"icon_id": "40001359",
"name": "suno",
"font_class": "suno",
"unicode": "e608",
"unicode_decimal": 58888
},
{
"icon_id": "4318807",
"name": "mp3",
"font_class": "mp",
"unicode": "e6c4",
"unicode_decimal": 59076
},
{
"icon_id": "12600802",
"name": "mp4",
"font_class": "mp1",
"unicode": "e647",
"unicode_decimal": 58951
},
{
"icon_id": "12243734",
"name": "control",

Binary file not shown.

View File

@ -0,0 +1,243 @@
<template>
<div class="player">
<div class="container">
<div class="cover">
<el-image :src="cover" fit="cover" />
</div>
<div class="info">
<div class="title">{{title}}</div>
<div class="style">
<span class="tags">{{ tags }}</span>
<span class="text-lightGray"> | </span>
<span class="time">{{ formatTime(currentTime) }} /{{ formatTime(duration) }}</span>
</div>
</div>
<div class="controls-container">
<div class="controls">
<button @click="prevSong" class="control-btn">
<i class="iconfont icon-prev"></i>
</button>
<button @click="togglePlay" class="control-btn">
<i class="iconfont icon-play" v-if="!isPlaying"></i>
<i class="iconfont icon-pause" v-else></i>
</button>
<button @click="nextSong" class="control-btn">
<i class="iconfont icon-next"></i>
</button>
</div>
</div>
<div class="progress-bar" @click="setProgress" ref="progressBarRef">
<div class="progress" :style="{ width: `${progressPercent}%` }"></div>
</div>
<audio ref="audio" @timeupdate="updateProgress" @ended="nextSong"></audio>
</div>
</div>
</template>
<script setup>
import {ref, onMounted, watch} from 'vue';
import {showMessageError} from "@/utils/dialog";
const audio = ref(null);
const isPlaying = ref(false);
const songIndex = ref(0);
const currentTime = ref(0);
const duration = ref(100);
const progressPercent = ref(0);
const progressBarRef = ref(null)
const title = ref("")
const tags = ref("")
const cover = ref("")
const props = defineProps({
songs: {
type: Array,
required: true,
default: () => []
},
});
watch(() => props.songs, (newVal) => {
console.log(newVal)
loadSong(newVal[songIndex.value]);
});
const loadSong = (song) => {
if (!song) {
showMessageError("歌曲加载失败")
return
}
title.value = song.title
tags.value = song.tags
cover.value = song.thumb_img_url
audio.value.src = song.audio_url;
audio.value.load();
audio.value.onloadedmetadata = () => {
duration.value = audio.value.duration;
};
};
const togglePlay = () => {
if (isPlaying.value) {
audio.value.pause();
} else {
audio.value.play();
}
isPlaying.value = !isPlaying.value;
};
const play = () => {
audio.value.play();
}
const prevSong = () => {
songIndex.value = (songIndex.value - 1 + props.songs.length) % props.songs.length;
loadSong(props.songs[songIndex.value]);
audio.value.play();
isPlaying.value = true;
};
const nextSong = () => {
songIndex.value = (songIndex.value + 1) % props.songs.length;
loadSong(props.songs[songIndex.value]);
audio.value.play();
isPlaying.value = true;
};
const updateProgress = () => {
currentTime.value = audio.value.currentTime;
progressPercent.value = (currentTime.value / duration.value) * 100;
};
const formatTime = (time) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
};
const setProgress = (event) => {
const totalWidth = progressBarRef.value.offsetWidth;
const clickX = event.offsetX;
const audioDuration = audio.value.duration;
audio.value.currentTime = (clickX / totalWidth) * audioDuration;
};
defineExpose({
play
});
onMounted(() => {
loadSong(props.songs[songIndex.value]);
});
</script>
<style lang="stylus" scoped>
.player {
display flex
justify-content center
width 100%
.container {
display flex
background-color: #363030;
border-radius: 10px;
border 1px solid #544F4F;
padding: 5px;
width: 80%
text-align: center;
position relative
overflow hidden
.cover {
.el-image {
border-radius: 50%;
width 50px
}
}
.info {
padding 0 10px
width 300px
display flex
justify-content center
align-items flex-start
flex-flow column
line-height 1.5
.title {
font-weight 700
font-size 16px
}
.style {
font-size 14px
.tags {
font-weight 600
}
.text-lightGray {
color: rgb(114 110 108);
padding 0 3px
}
.time {
font-family 'Input Sans'
font-weight 700
}
}
}
.controls-container {
width 100%
display flex
flex-flow column
justify-content center
.controls {
display: flex;
justify-content: space-around;
margin-bottom 10px
.control-btn {
background: none;
border: none;
color: #fff;
cursor: pointer;
background-color #363030
border-radius 5px
padding 6px
.iconfont {
font-size 20px
}
&:hover {
background-color #5F5958
}
}
}
}
.progress-bar {
position absolute
width 100%
left 0
bottom 0
height: 8px;
background-color: #555;
cursor: pointer;
.progress {
height: 100%;
background-color: #f50;
border-radius: 5px;
width: 0;
}
}
}
}
</style>

View File

@ -339,7 +339,6 @@ const downloadImage = () => {
a.download = "geek-ai-xmind.png"
a.href = canvas.toDataURL(`image/png`)
a.click()
}
}

View File

@ -96,18 +96,21 @@
</div>
</div>
</div>
<div class="right-box">
<div class="list-box" v-if="list.length > 0">
<div class="right-box" v-loading="loading" element-loading-background="rgba(100,100,100,0.3)">
<div class="list-box" v-if="!noData">
<div class="item" v-for="item in list">
<div class="left">
<div class="container">
<el-image :src="item.thumb_img_url" fit="cover" />
<div class="duration">{{duration(item.duration)}}</div>
<button class="play" @click="play(item)">
<img src="/images/play.svg" alt=""/>
</button>
</div>
</div>
<div class="center">
<div class="title">
<span>{{item.title}}</span>
<a href="/song/xxxxx">{{item.title}}</a>
<span class="model">{{item.model}}</span>
</div>
<div class="tags">{{item.tags}}</div>
@ -117,22 +120,58 @@
<el-tooltip effect="light" content="以当前歌曲为素材继续创作" placement="top">
<button class="btn">续写</button>
</el-tooltip>
<button class="btn btn-publish">
<span class="text">发布</span>
<black-switch v-model:value="item.publish" size="small" />
</button>
<el-tooltip effect="light" content="下载歌曲" placement="top">
<a :href="item.audio_url" :download="item.title+'.mp3'">
<button class="btn btn-icon">
<i class="iconfont icon-download"></i>
</button>
</a>
</el-tooltip>
<el-tooltip effect="light" content="复制歌曲链接" placement="top">
<button class="btn btn-icon">
<i class="iconfont icon-share1"></i>
</button>
</el-tooltip>
<el-tooltip effect="light" content="编辑" placement="top">
<button class="btn btn-icon">
<i class="iconfont icon-edit"></i>
</button>
</el-tooltip>
<el-tooltip effect="light" content="删除" placement="top">
<button class="btn btn-icon">
<i class="iconfont icon-remove"></i>
</button>
</el-tooltip>
</div>
</div>
</div>
</div>
<el-empty :image-size="100" description="没有任何作品,赶紧去创作吧!" v-else/>
<div class="music-player" v-if="playList.length > 0">
<music-player :songs="playList" ref="playerRef" />
</div>
</div>
</div>
</template>
<script setup>
import {ref} from "vue"
import {nextTick, ref} from "vue"
import {InfoFilled} from "@element-plus/icons-vue";
import BlackSelect from "@/components/ui/BlackSelect.vue";
import BlackSwitch from "@/components/ui/BlackSwitch.vue";
import BlackInput from "@/components/ui/BlackInput.vue";
import ImageDall from "@/views/mobile/pages/ImageDall.vue";
import MusicPlayer from "@/components/MusicPlayer.vue";
import {compact} from "lodash";
const winHeight = ref(window.innerHeight - 60)
const custom = ref(false)
@ -148,7 +187,10 @@ const data = ref({
prompt: "",
title: ""
})
const loading = ref(false)
const noData = ref(false)
const playList = ref([])
const playerRef = ref(null)
const list = ref([
{
id: 1,
@ -157,6 +199,7 @@ const list = ref([
tags: "uplifting pop",
thumb_img_url: "https://cdn2.suno.ai/image_047796ce-7bf3-4051-a59c-66ce60448ff2.jpeg?width=100",
audio_url: "/files/suno.mp3",
publish: true,
duration: 134,
},
{
@ -164,8 +207,9 @@ const list = ref([
title: "我是一个鸟人",
model: "v3",
tags: "摇滚",
publish: false,
thumb_img_url: "https://cdn2.suno.ai/image_e5d25fd0-06a5-4cd7-910c-4b62872cd503.jpeg?width=100",
audio_url: "/files/suno.mp3",
audio_url: "/files/test.mp3",
duration: 194,
},
{
@ -173,6 +217,7 @@ const list = ref([
title: "鸟人传说 (Birdman Legend)",
model: "v3",
tags: "uplifting pop",
publish: true,
thumb_img_url: "https://cdn2.suno.ai/image_047796ce-7bf3-4051-a59c-66ce60448ff2.jpeg?width=100",
audio_url: "/files/suno.mp3",
duration: 138,
@ -182,6 +227,7 @@ const list = ref([
title: "我是一个鸟人",
model: "v3",
tags: "摇滚",
publish: false,
thumb_img_url: "https://cdn2.suno.ai/image_e5d25fd0-06a5-4cd7-910c-4b62872cd503.jpeg?width=100",
audio_url: "/files/suno.mp3",
duration: 144,
@ -192,6 +238,10 @@ const create = () => {
console.log(data.value)
}
const play = (item) => {
playList.value = [item]
nextTick(()=> playerRef.value.play())
}
//
const duration = (secs) => {
const minutes =Math.floor(secs/60)