mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-23 23:57:22 +08:00
✨ feat: 添加eslint 和 桌面歌词(未完成)
This commit is contained in:
@@ -8,13 +8,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-menu-list">
|
||||
<div class="app-menu-item" v-for="(item,index) in menus">
|
||||
<div v-for="(item, index) in menus" :key="item.path" class="app-menu-item">
|
||||
<router-link class="app-menu-item-link" :to="item.path">
|
||||
<i
|
||||
class="iconfont app-menu-item-icon"
|
||||
:style="iconStyle(index)"
|
||||
:class="item.meta.icon"
|
||||
></i>
|
||||
<i class="iconfont app-menu-item-icon" :style="iconStyle(index)" :class="item.meta.icon"></i>
|
||||
<span v-if="isText" class="app-menu-item-text ml-3">{{ item.meta.title }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
@@ -24,44 +20,47 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRoute } from "vue-router";
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const props = defineProps({
|
||||
isText: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: '26px'
|
||||
default: '26px',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#aaa'
|
||||
default: '#aaa',
|
||||
},
|
||||
selectColor: {
|
||||
type: String,
|
||||
default: '#10B981'
|
||||
default: '#10B981',
|
||||
},
|
||||
menus: {
|
||||
type: Array as any,
|
||||
default: []
|
||||
}
|
||||
})
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const path = ref(route.path);
|
||||
watch(() => route.path, async newParams => {
|
||||
path.value = newParams
|
||||
})
|
||||
watch(
|
||||
() => route.path,
|
||||
async (newParams) => {
|
||||
path.value = newParams;
|
||||
},
|
||||
);
|
||||
|
||||
const iconStyle = (index: any) => {
|
||||
let style = {
|
||||
const style = {
|
||||
fontSize: props.size,
|
||||
color: path.value === props.menus[index].path ? props.selectColor : props.color
|
||||
}
|
||||
return style
|
||||
}
|
||||
|
||||
color: path.value === props.menus[index].path ? props.selectColor : props.color,
|
||||
};
|
||||
return style;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -83,4 +82,4 @@ const iconStyle = (index: any) => {
|
||||
transform: scale(1.05);
|
||||
transition: 0.2s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,33 +1,25 @@
|
||||
<template>
|
||||
<n-drawer
|
||||
:show="musicFull"
|
||||
height="100vh"
|
||||
placement="bottom"
|
||||
:drawer-style="{ backgroundColor: 'transparent' }"
|
||||
>
|
||||
<n-drawer :show="musicFull" height="100vh" placement="bottom" :drawer-style="{ backgroundColor: 'transparent' }">
|
||||
<div id="drawer-target">
|
||||
<div class="drawer-back" :class="{'paused': !isPlaying}" :style="{backgroundImage:`url(${getImgUrl(playMusic?.picUrl, '300y300')})`}"></div>
|
||||
<div
|
||||
class="drawer-back"
|
||||
:class="{ paused: !isPlaying }"
|
||||
:style="{ backgroundImage: `url(${getImgUrl(playMusic?.picUrl, '300y300')})` }"
|
||||
></div>
|
||||
<div class="music-img">
|
||||
<n-image
|
||||
ref="PicImgRef"
|
||||
:src="getImgUrl(playMusic?.picUrl, '300y300')"
|
||||
class="img"
|
||||
lazy
|
||||
preview-disabled
|
||||
/>
|
||||
<n-image ref="PicImgRef" :src="getImgUrl(playMusic?.picUrl, '300y300')" class="img" lazy preview-disabled />
|
||||
</div>
|
||||
<div class="music-content">
|
||||
<div class="music-content-name">{{ playMusic.song.name }}</div>
|
||||
<div class="music-content-singer">
|
||||
<span v-for="(item, index) in playMusic.song.artists" :key="index">
|
||||
{{ item.name
|
||||
}}{{ index < playMusic.song.artists.length - 1 ? ' / ' : '' }}
|
||||
{{ item.name }}{{ index < playMusic.song.artists.length - 1 ? ' / ' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
<n-layout
|
||||
ref="lrcSider"
|
||||
class="music-lrc"
|
||||
style="height: 55vh"
|
||||
ref="lrcSider"
|
||||
:native-scrollbar="false"
|
||||
@mouseover="mouseOverLayout"
|
||||
@mouseleave="mouseLeaveLayout"
|
||||
@@ -38,7 +30,8 @@
|
||||
:class="{ 'now-text': isCurrentLrc(index, nowTime) }"
|
||||
@click="setAudioTime(index, audio)"
|
||||
>
|
||||
{{ item.text }}
|
||||
<div>{{ item.text }}</div>
|
||||
<div class="music-lrc-text-tr">{{ item.trText }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</n-layout>
|
||||
@@ -53,20 +46,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { SongResult } from '@/type/music'
|
||||
import { getImgUrl } from '@/utils'
|
||||
import { useStore } from 'vuex'
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
import {
|
||||
addCorrectionTime,
|
||||
isCurrentLrc,
|
||||
lrcArray,
|
||||
newLrcIndex,
|
||||
isCurrentLrc,
|
||||
addCorrectionTime,
|
||||
nowTime,
|
||||
reduceCorrectionTime,
|
||||
setAudioTime,
|
||||
nowTime,
|
||||
} from '@/hooks/MusicHook'
|
||||
} from '@/hooks/MusicHook';
|
||||
import type { SongResult } from '@/type/music';
|
||||
import { getImgUrl } from '@/utils';
|
||||
|
||||
const store = useStore()
|
||||
const store = useStore();
|
||||
|
||||
const props = defineProps({
|
||||
musicFull: {
|
||||
@@ -77,39 +71,36 @@ const props = defineProps({
|
||||
type: HTMLAudioElement,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:musicFull'])
|
||||
});
|
||||
|
||||
// 播放的音乐信息
|
||||
const playMusic = computed(() => store.state.playMusic as SongResult)
|
||||
const isPlaying = computed(() => store.state.play as boolean)
|
||||
const playMusic = computed(() => store.state.playMusic as SongResult);
|
||||
const isPlaying = computed(() => store.state.play as boolean);
|
||||
// 获取歌词滚动dom
|
||||
const lrcSider = ref<any>(null)
|
||||
const isMouse = ref(false)
|
||||
const lrcSider = ref<any>(null);
|
||||
const isMouse = ref(false);
|
||||
// 歌词滚动方法
|
||||
const lrcScroll = () => {
|
||||
if (props.musicFull && !isMouse.value) {
|
||||
let top = newLrcIndex.value * 50 - 225
|
||||
lrcSider.value.scrollTo({ top: top, behavior: 'smooth' })
|
||||
const top = newLrcIndex.value * 60 - 225;
|
||||
lrcSider.value.scrollTo({ top, behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
};
|
||||
const mouseOverLayout = () => {
|
||||
isMouse.value = true
|
||||
}
|
||||
isMouse.value = true;
|
||||
};
|
||||
const mouseLeaveLayout = () => {
|
||||
setTimeout(() => {
|
||||
isMouse.value = false
|
||||
}, 3000)
|
||||
}
|
||||
isMouse.value = false;
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
lrcScroll,
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@keyframes round {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
@@ -118,7 +109,7 @@ defineExpose({
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.drawer-back{
|
||||
.drawer-back {
|
||||
@apply absolute bg-cover bg-center opacity-70;
|
||||
filter: blur(80px) brightness(80%);
|
||||
z-index: -1;
|
||||
@@ -161,28 +152,30 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.music-content-time{
|
||||
.music-content-time {
|
||||
display: none;
|
||||
@apply flex justify-center items-center;
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
|
||||
.music-lrc {
|
||||
background-color: inherit;
|
||||
width: 500px;
|
||||
height: 550px;
|
||||
|
||||
.now-text {
|
||||
@apply text-red-500;
|
||||
}
|
||||
&-text {
|
||||
@apply text-white text-lg flex justify-center items-center cursor-pointer;
|
||||
height: 50px;
|
||||
@apply text-white text-lg flex flex-col justify-center items-center cursor-pointer font-bold;
|
||||
height: 60px;
|
||||
transition: all 0.2s ease-out;
|
||||
|
||||
&:hover {
|
||||
@apply font-bold text-xl text-red-500;
|
||||
@apply font-bold text-red-500;
|
||||
}
|
||||
}
|
||||
|
||||
.now-text {
|
||||
@apply font-bold text-xl text-red-500;
|
||||
&-tr {
|
||||
@apply text-sm font-normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<!-- 展开全屏 -->
|
||||
<music-full ref="MusicFullRef" v-model:musicFull="musicFull" :audio="(audio.value as HTMLAudioElement)" />
|
||||
<music-full ref="MusicFullRef" v-model:musicFull="musicFull" :audio="audio.value as HTMLAudioElement" />
|
||||
<!-- 底部播放栏 -->
|
||||
<div class="music-play-bar" :class="setAnimationClass('animate__bounceInUp')">
|
||||
<n-image
|
||||
@@ -19,8 +19,7 @@
|
||||
<div class="music-content-name">
|
||||
<n-ellipsis class="text-ellipsis" line-clamp="1">
|
||||
<span v-for="(item, index) in playMusic.song.artists" :key="index">
|
||||
{{ item.name
|
||||
}}{{ index < playMusic.song.artists.length - 1 ? ' / ' : '' }}
|
||||
{{ item.name }}{{ index < playMusic.song.artists.length - 1 ? ' / ' : '' }}
|
||||
</span>
|
||||
</n-ellipsis>
|
||||
</div>
|
||||
@@ -38,22 +37,14 @@
|
||||
</div>
|
||||
<div class="music-time">
|
||||
<div class="time">{{ getNowTime }}</div>
|
||||
<n-slider
|
||||
v-model:value="timeSlider"
|
||||
:step="0.05"
|
||||
:tooltip="false"
|
||||
></n-slider>
|
||||
<n-slider v-model:value="timeSlider" :step="0.05" :tooltip="false"></n-slider>
|
||||
<div class="time">{{ getAllTime }}</div>
|
||||
</div>
|
||||
<div class="audio-volume">
|
||||
<div>
|
||||
<i class="iconfont icon-notificationfill"></i>
|
||||
</div>
|
||||
<n-slider
|
||||
v-model:value="volumeSlider"
|
||||
:step="0.01"
|
||||
:tooltip="false"
|
||||
></n-slider>
|
||||
<n-slider v-model:value="volumeSlider" :step="0.01" :tooltip="false"></n-slider>
|
||||
</div>
|
||||
<div class="audio-button">
|
||||
<!-- <n-tooltip trigger="hover" :z-index="9999999">
|
||||
@@ -68,12 +59,12 @@
|
||||
</template>
|
||||
解析播放
|
||||
</n-tooltip> -->
|
||||
<!-- <n-tooltip trigger="hover" :z-index="9999999">
|
||||
<n-tooltip trigger="hover" :z-index="9999999">
|
||||
<template #trigger>
|
||||
<i class="iconfont icon-full" @click="setMusicFull"></i>
|
||||
<i class="iconfont ri-netease-cloud-music-line" @click="openLyric"></i>
|
||||
</template>
|
||||
歌词
|
||||
</n-tooltip> -->
|
||||
</n-tooltip>
|
||||
<n-popover trigger="click" :z-index="99999999" content-class="music-play" raw :show-arrow="false" :delay="200">
|
||||
<template #trigger>
|
||||
<n-tooltip trigger="manual" :z-index="9999999">
|
||||
@@ -87,154 +78,143 @@
|
||||
<div class="music-play-list-back"></div>
|
||||
<n-scrollbar>
|
||||
<div class="music-play-list-content">
|
||||
<song-item v-for="(item, index) in playList" :key="item.id" :item="item" mini></song-item>
|
||||
<song-item v-for="item in playList" :key="item.id" :item="item" mini></song-item>
|
||||
</div>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</n-popover>
|
||||
</div>
|
||||
<!-- 播放音乐 -->
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { SongResult } from '@/type/music'
|
||||
import { secondToMinute, getImgUrl } from '@/utils'
|
||||
import { useStore } from 'vuex'
|
||||
import { setAnimationClass } from '@/utils'
|
||||
import {
|
||||
loadLrc,
|
||||
nowTime,
|
||||
allTime
|
||||
} from '@/hooks/MusicHook'
|
||||
import MusicFull from './MusicFull.vue'
|
||||
import SongItem from '@/components/common/SongItem.vue'
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
const store = useStore()
|
||||
import SongItem from '@/components/common/SongItem.vue';
|
||||
import { allTime, loadLrc, nowTime, openLyric, sendLyricToWin } from '@/hooks/MusicHook';
|
||||
import type { SongResult } from '@/type/music';
|
||||
import { getImgUrl, secondToMinute, setAnimationClass } from '@/utils';
|
||||
|
||||
import MusicFull from './MusicFull.vue';
|
||||
|
||||
const store = useStore();
|
||||
|
||||
// 播放的音乐信息
|
||||
const playMusic = computed(() => store.state.playMusic as SongResult)
|
||||
const playMusic = computed(() => store.state.playMusic as SongResult);
|
||||
// 是否播放
|
||||
const play = computed(() => store.state.play as boolean)
|
||||
const play = computed(() => store.state.play as boolean);
|
||||
|
||||
const playList = computed(() => store.state.playList as SongResult[])
|
||||
const playList = computed(() => store.state.playList as SongResult[]);
|
||||
|
||||
const audio = {
|
||||
value: document.querySelector('#MusicAudio') as HTMLAudioElement
|
||||
}
|
||||
value: document.querySelector('#MusicAudio') as HTMLAudioElement,
|
||||
};
|
||||
|
||||
watch(
|
||||
() => store.state.playMusicUrl,
|
||||
() => {
|
||||
loadLrc(playMusic.value.id)
|
||||
loadLrc(playMusic.value.id);
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const audioPlay = () => {
|
||||
if (audio.value) {
|
||||
audio.value.play()
|
||||
audio.value.play();
|
||||
}
|
||||
}
|
||||
|
||||
const audioPause = () => {
|
||||
if (audio.value) {
|
||||
audio.value.pause()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 计算属性 获取当前播放时间的进度
|
||||
const timeSlider = computed({
|
||||
get: () => (nowTime.value / allTime.value) * 100,
|
||||
set: (value) => {
|
||||
if (!audio.value) return
|
||||
audio.value.currentTime = (value * allTime.value) / 100
|
||||
audioPlay()
|
||||
store.commit('setPlayMusic', true)
|
||||
if (!audio.value) return;
|
||||
audio.value.currentTime = (value * allTime.value) / 100;
|
||||
audioPlay();
|
||||
store.commit('setPlayMusic', true);
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
// 音量条
|
||||
const audioVolume = ref(1)
|
||||
const audioVolume = ref(1);
|
||||
const volumeSlider = computed({
|
||||
get: () => audioVolume.value * 100,
|
||||
set: (value) => {
|
||||
if(!audio.value) return
|
||||
audio.value.volume = value / 100
|
||||
if (!audio.value) return;
|
||||
audio.value.volume = value / 100;
|
||||
},
|
||||
})
|
||||
});
|
||||
// 获取当前播放时间
|
||||
const getNowTime = computed(() => {
|
||||
return secondToMinute(nowTime.value)
|
||||
})
|
||||
return secondToMinute(nowTime.value);
|
||||
});
|
||||
|
||||
// 获取总时间
|
||||
const getAllTime = computed(() => {
|
||||
return secondToMinute(allTime.value)
|
||||
})
|
||||
return secondToMinute(allTime.value);
|
||||
});
|
||||
|
||||
// 监听音乐播放 获取时间
|
||||
const onAudio = () => {
|
||||
if(audio.value){
|
||||
audio.value.removeEventListener('timeupdate', handleGetAudioTime)
|
||||
audio.value.removeEventListener('ended', handleEnded)
|
||||
audio.value.addEventListener('timeupdate', handleGetAudioTime)
|
||||
audio.value.addEventListener('ended', handleEnded)
|
||||
if (audio.value) {
|
||||
audio.value.removeEventListener('timeupdate', handleGetAudioTime);
|
||||
audio.value.removeEventListener('ended', handleEnded);
|
||||
audio.value.addEventListener('timeupdate', handleGetAudioTime);
|
||||
audio.value.addEventListener('ended', handleEnded);
|
||||
// 监听音乐播放暂停
|
||||
audio.value.addEventListener('pause', () => {
|
||||
store.commit('setPlayMusic', false)
|
||||
})
|
||||
store.commit('setPlayMusic', false);
|
||||
});
|
||||
audio.value.addEventListener('play', () => {
|
||||
store.commit('setPlayMusic', true)
|
||||
})
|
||||
store.commit('setPlayMusic', true);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onAudio()
|
||||
onAudio();
|
||||
|
||||
function handleEnded() {
|
||||
store.commit('nextPlay')
|
||||
store.commit('nextPlay');
|
||||
}
|
||||
|
||||
function handlePrev() {
|
||||
store.commit('prevPlay')
|
||||
store.commit('prevPlay');
|
||||
}
|
||||
|
||||
const MusicFullRef = ref<any>(null)
|
||||
const MusicFullRef = ref<any>(null);
|
||||
|
||||
function handleGetAudioTime(this: any) {
|
||||
// 监听音频播放的实时时间事件
|
||||
const audio = this as HTMLAudioElement
|
||||
const audio = this as HTMLAudioElement;
|
||||
// 获取当前播放时间
|
||||
nowTime.value = Math.floor(audio.currentTime)
|
||||
nowTime.value = Math.floor(audio.currentTime);
|
||||
// 获取总时间
|
||||
allTime.value = audio.duration
|
||||
allTime.value = audio.duration;
|
||||
// 获取音量
|
||||
audioVolume.value = audio.volume
|
||||
MusicFullRef.value?.lrcScroll()
|
||||
audioVolume.value = audio.volume;
|
||||
sendLyricToWin(store.state.isPlay);
|
||||
MusicFullRef.value?.lrcScroll();
|
||||
}
|
||||
|
||||
// 播放暂停按钮事件
|
||||
const playMusicEvent = async () => {
|
||||
if (play.value) {
|
||||
store.commit('setPlayMusic', false)
|
||||
store.commit('setPlayMusic', false);
|
||||
} else {
|
||||
store.commit('setPlayMusic', true)
|
||||
store.commit('setPlayMusic', true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const musicFull = ref(false)
|
||||
const musicFull = ref(false);
|
||||
|
||||
// 设置musicFull
|
||||
const setMusicFull = () => {
|
||||
musicFull.value = !musicFull.value
|
||||
}
|
||||
|
||||
musicFull.value = !musicFull.value;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.text-ellipsis {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -243,7 +223,9 @@ const setMusicFull = () => {
|
||||
@apply h-20 w-full absolute bottom-0 left-0 flex items-center rounded-t-2xl overflow-hidden box-border px-6 py-2;
|
||||
z-index: 9999;
|
||||
box-shadow: 0px 0px 10px 2px rgba(203, 203, 203, 0.034);
|
||||
background-color: rgba(0, 0, 0, 0.747); .music-content {
|
||||
background-color: rgba(0, 0, 0, 0.747);
|
||||
animation-duration: 0.5s !important;
|
||||
.music-content {
|
||||
width: 140px;
|
||||
@apply ml-4;
|
||||
|
||||
@@ -310,16 +292,15 @@ const setMusicFull = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.music-play{
|
||||
|
||||
&-list{
|
||||
.music-play {
|
||||
&-list {
|
||||
height: 50vh;
|
||||
@apply relative rounded-3xl overflow-hidden;
|
||||
&-back{
|
||||
&-back {
|
||||
backdrop-filter: blur(20px);
|
||||
@apply absolute top-0 left-0 w-full h-full bg-gray-800 bg-opacity-75;
|
||||
}
|
||||
&-content{
|
||||
&-content {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
+161
-168
@@ -1,168 +1,161 @@
|
||||
<template>
|
||||
<div class="search-box flex">
|
||||
<div class="search-box-input flex-1">
|
||||
<n-input
|
||||
size="medium"
|
||||
round
|
||||
v-model:value="searchValue"
|
||||
:placeholder="hotSearchKeyword"
|
||||
class="border border-gray-600"
|
||||
@keydown.enter="search"
|
||||
>
|
||||
<template #prefix>
|
||||
<i class="iconfont icon-search"></i>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<div class="w-20 px-3 flex justify-between items-center">
|
||||
<div>{{ searchTypeOptions.find(item => item.key === searchType)?.label }}</div>
|
||||
<n-dropdown trigger="hover" @select="selectSearchType" :options="searchTypeOptions">
|
||||
<i class="iconfont icon-xiasanjiaoxing"></i>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</n-input>
|
||||
</div>
|
||||
<div class="user-box">
|
||||
<n-dropdown trigger="hover" @select="selectItem" :options="userSetOptions">
|
||||
<i class="iconfont icon-xiasanjiaoxing"></i>
|
||||
</n-dropdown>
|
||||
<n-avatar
|
||||
class="ml-2 cursor-pointer"
|
||||
circle
|
||||
size="medium"
|
||||
:src="getImgUrl(store.state.user.avatarUrl)"
|
||||
v-if="store.state.user"
|
||||
/>
|
||||
<div class="mx-2 rounded-full cursor-pointer text-sm" v-else @click="toLogin">登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getSearchKeyword } from '@/api/home';
|
||||
import { getUserDetail, logout } from '@/api/login';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
import request from '@/utils/request_mt'
|
||||
import { getImgUrl } from '@/utils';
|
||||
import {USER_SET_OPTIONS, SEARCH_TYPES} from '@/const/bar-const'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useStore();
|
||||
const userSetOptions = ref(USER_SET_OPTIONS)
|
||||
|
||||
|
||||
// 推荐热搜词
|
||||
const hotSearchKeyword = ref("搜索点什么吧...")
|
||||
const hotSearchValue = ref("")
|
||||
const loadHotSearchKeyword = async () => {
|
||||
const { data } = await getSearchKeyword();
|
||||
hotSearchKeyword.value = data.data.showKeyword
|
||||
hotSearchValue.value = data.data.realkeyword
|
||||
}
|
||||
|
||||
const loadPage = async () => {
|
||||
const token = localStorage.getItem("token")
|
||||
if (!token) return
|
||||
const { data } = await getUserDetail()
|
||||
store.state.user = data.profile
|
||||
localStorage.setItem('user', JSON.stringify(data.profile))
|
||||
}
|
||||
|
||||
|
||||
watchEffect(() => {
|
||||
loadPage()
|
||||
if (store.state.user) {
|
||||
userSetOptions.value = USER_SET_OPTIONS
|
||||
} else {
|
||||
userSetOptions.value = USER_SET_OPTIONS.filter(item => item.key !== 'logout')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const toLogin = () => {
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadHotSearchKeyword()
|
||||
loadPage()
|
||||
})
|
||||
|
||||
|
||||
// 搜索词
|
||||
const searchValue = ref("")
|
||||
const searchType = ref(1)
|
||||
const search = () => {
|
||||
let value = searchValue.value
|
||||
if (value == "") {
|
||||
searchValue.value = hotSearchValue.value
|
||||
} else {
|
||||
router.push({
|
||||
path: "/search",
|
||||
query: {
|
||||
keyword: value,
|
||||
type: searchType.value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const selectSearchType = (key: any) => {
|
||||
searchType.value = key
|
||||
}
|
||||
|
||||
|
||||
const searchTypeOptions = ref(SEARCH_TYPES)
|
||||
|
||||
const selectItem = async (key: any) => {
|
||||
// switch 判断
|
||||
switch (key) {
|
||||
case 'card':
|
||||
await request.get('/?do=sign')
|
||||
.then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
break;
|
||||
case 'card_music':
|
||||
await request.get('/?do=daka')
|
||||
.then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
break;
|
||||
case 'listen':
|
||||
await request.get('/?do=listen&id=1885175990&time=300')
|
||||
.then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
break;
|
||||
case 'logout':
|
||||
logout().then(() => {
|
||||
store.state.user = null
|
||||
localStorage.clear()
|
||||
})
|
||||
break;
|
||||
case 'login':
|
||||
router.push("/login")
|
||||
break;
|
||||
case 'set':
|
||||
router.push("/set")
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-box {
|
||||
@apply ml-4 flex text-lg justify-center items-center rounded-full pl-3 border border-gray-600;
|
||||
background: #1a1a1a;
|
||||
}
|
||||
.search-box{
|
||||
@apply pb-4 pr-4;
|
||||
}
|
||||
.search-box-input {
|
||||
@apply relative;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="search-box flex">
|
||||
<div class="search-box-input flex-1">
|
||||
<n-input
|
||||
v-model:value="searchValue"
|
||||
size="medium"
|
||||
round
|
||||
:placeholder="hotSearchKeyword"
|
||||
class="border border-gray-600"
|
||||
@keydown.enter="search"
|
||||
>
|
||||
<template #prefix>
|
||||
<i class="iconfont icon-search"></i>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<div class="w-20 px-3 flex justify-between items-center">
|
||||
<div>{{ searchTypeOptions.find((item) => item.key === searchType)?.label }}</div>
|
||||
<n-dropdown trigger="hover" :options="searchTypeOptions" @select="selectSearchType">
|
||||
<i class="iconfont icon-xiasanjiaoxing"></i>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</n-input>
|
||||
</div>
|
||||
<div class="user-box">
|
||||
<n-dropdown trigger="hover" :options="userSetOptions" @select="selectItem">
|
||||
<i class="iconfont icon-xiasanjiaoxing"></i>
|
||||
</n-dropdown>
|
||||
<n-avatar
|
||||
v-if="store.state.user"
|
||||
class="ml-2 cursor-pointer"
|
||||
circle
|
||||
size="medium"
|
||||
:src="getImgUrl(store.state.user.avatarUrl)"
|
||||
/>
|
||||
<div v-else class="mx-2 rounded-full cursor-pointer text-sm" @click="toLogin">登录</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
|
||||
import { getSearchKeyword } from '@/api/home';
|
||||
import { getUserDetail, logout } from '@/api/login';
|
||||
import { SEARCH_TYPES, USER_SET_OPTIONS } from '@/const/bar-const';
|
||||
import { getImgUrl } from '@/utils';
|
||||
import request from '@/utils/request_mt';
|
||||
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const userSetOptions = ref(USER_SET_OPTIONS);
|
||||
|
||||
// 推荐热搜词
|
||||
const hotSearchKeyword = ref('搜索点什么吧...');
|
||||
const hotSearchValue = ref('');
|
||||
const loadHotSearchKeyword = async () => {
|
||||
const { data } = await getSearchKeyword();
|
||||
hotSearchKeyword.value = data.data.showKeyword;
|
||||
hotSearchValue.value = data.data.realkeyword;
|
||||
};
|
||||
|
||||
const loadPage = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) return;
|
||||
const { data } = await getUserDetail();
|
||||
store.state.user = data.profile;
|
||||
localStorage.setItem('user', JSON.stringify(data.profile));
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
loadPage();
|
||||
if (store.state.user) {
|
||||
userSetOptions.value = USER_SET_OPTIONS;
|
||||
} else {
|
||||
userSetOptions.value = USER_SET_OPTIONS.filter((item) => item.key !== 'logout');
|
||||
}
|
||||
});
|
||||
|
||||
const toLogin = () => {
|
||||
router.push('/login');
|
||||
};
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadHotSearchKeyword();
|
||||
loadPage();
|
||||
});
|
||||
|
||||
// 搜索词
|
||||
const searchValue = ref('');
|
||||
const searchType = ref(1);
|
||||
const search = () => {
|
||||
const { value } = searchValue;
|
||||
if (value === '') {
|
||||
searchValue.value = hotSearchValue.value;
|
||||
} else {
|
||||
router.push({
|
||||
path: '/search',
|
||||
query: {
|
||||
keyword: value,
|
||||
type: searchType.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const selectSearchType = (key: any) => {
|
||||
searchType.value = key;
|
||||
};
|
||||
|
||||
const searchTypeOptions = ref(SEARCH_TYPES);
|
||||
|
||||
const selectItem = async (key: any) => {
|
||||
// switch 判断
|
||||
switch (key) {
|
||||
case 'card':
|
||||
await request.get('/?do=sign').then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
break;
|
||||
case 'card_music':
|
||||
await request.get('/?do=daka').then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
break;
|
||||
case 'listen':
|
||||
await request.get('/?do=listen&id=1885175990&time=300').then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
break;
|
||||
case 'logout':
|
||||
logout().then(() => {
|
||||
store.state.user = null;
|
||||
localStorage.clear();
|
||||
});
|
||||
break;
|
||||
case 'login':
|
||||
router.push('/login');
|
||||
break;
|
||||
case 'set':
|
||||
router.push('/set');
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-box {
|
||||
@apply ml-4 flex text-lg justify-center items-center rounded-full pl-3 border border-gray-600;
|
||||
background: #1a1a1a;
|
||||
}
|
||||
.search-box {
|
||||
@apply pb-4 pr-4;
|
||||
}
|
||||
.search-box-input {
|
||||
@apply relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
<button @click="minimize">
|
||||
<i class="iconfont icon-minisize"></i>
|
||||
</button>
|
||||
<!-- <button @click="maximize">
|
||||
<i class="iconfont icon-maxsize"></i>
|
||||
</button> -->
|
||||
<button @click="close">
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
@@ -16,18 +13,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDialog } from 'naive-ui'
|
||||
import { useDialog } from 'naive-ui';
|
||||
|
||||
const dialog = useDialog()
|
||||
const windowData = window as any
|
||||
const dialog = useDialog();
|
||||
const windowData = window as any;
|
||||
|
||||
const minimize = () => {
|
||||
windowData.electronAPI.minimize()
|
||||
}
|
||||
|
||||
const maximize = () => {
|
||||
windowData.electronAPI.maximize()
|
||||
}
|
||||
windowData.electronAPI.minimize();
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
dialog.warning({
|
||||
@@ -36,17 +29,17 @@ const close = () => {
|
||||
positiveText: '最小化',
|
||||
negativeText: '关闭',
|
||||
onPositiveClick: () => {
|
||||
windowData.electronAPI.miniTray()
|
||||
windowData.electronAPI.miniTray();
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
windowData.electronAPI.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
windowData.electronAPI.close();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const drag = (event: MouseEvent) => {
|
||||
windowData.electronAPI.dragStart(event)
|
||||
}
|
||||
windowData.electronAPI.dragStart(event);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppMenu from "./AppMenu.vue";
|
||||
import PlayBar from "./PlayBar.vue";
|
||||
import SearchBar from "./SearchBar.vue";
|
||||
import AppMenu from './AppMenu.vue';
|
||||
import PlayBar from './PlayBar.vue';
|
||||
import SearchBar from './SearchBar.vue';
|
||||
|
||||
export { AppMenu, PlayBar, SearchBar };
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
defineProps({
|
||||
lrcList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: () => [],
|
||||
},
|
||||
lrcIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
lrcTime: {
|
||||
type: Number,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
Reference in New Issue
Block a user