APlayer内部组件ts改造
This commit is contained in:
parent
34b92fbd1a
commit
b49bebf654
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib"
|
||||||
|
}
|
||||||
@ -33,4 +33,10 @@ export interface MusicPlayerItem {
|
|||||||
src: string
|
src: string
|
||||||
pic: string
|
pic: string
|
||||||
lrc?: string
|
lrc?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatType {
|
||||||
|
duration: number,
|
||||||
|
loadedTime: number,
|
||||||
|
playedTime: number,
|
||||||
}
|
}
|
||||||
@ -2,7 +2,6 @@
|
|||||||
<div
|
<div
|
||||||
class="aplayer-bar-wrap"
|
class="aplayer-bar-wrap"
|
||||||
@mousedown="onThumbMouseDown"
|
@mousedown="onThumbMouseDown"
|
||||||
@touchstart="onThumbTouchStart"
|
|
||||||
ref="barWrap"
|
ref="barWrap"
|
||||||
>
|
>
|
||||||
<div class="aplayer-bar">
|
<div class="aplayer-bar">
|
||||||
@ -21,10 +20,8 @@
|
|||||||
class="aplayer-thumb"
|
class="aplayer-thumb"
|
||||||
:style="{borderColor: theme, backgroundColor: thumbHovered ? theme : '#fff'}"
|
:style="{borderColor: theme, backgroundColor: thumbHovered ? theme : '#fff'}"
|
||||||
>
|
>
|
||||||
<span class="aplayer-loading-icon"
|
<span class="aplayer-loading-icon" :style="{backgroundColor: theme }">
|
||||||
:style="{backgroundColor: theme }"
|
<player-icon type="loading"/>
|
||||||
>
|
|
||||||
<icon type="loading"/>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -32,169 +29,125 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {getElementViewLeft} from '../utils'
|
import { ref } from 'vue'
|
||||||
import Icon from './aplayer-icon.vue'
|
import { getElementViewLeft } from '../utils'
|
||||||
|
import PlayerIcon from './aplayer-icon.vue'
|
||||||
|
|
||||||
export default {
|
defineProps<{
|
||||||
components: {
|
loadProgress: number,
|
||||||
Icon
|
playProgress: number,
|
||||||
},
|
theme?: string
|
||||||
props: ['loadProgress', 'playProgress', 'theme'],
|
}>()
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
thumbHovered: false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onThumbMouseDown (e) {
|
|
||||||
const barWidth = this.$refs.barWrap.clientWidth
|
|
||||||
let percentage = (e.clientX - getElementViewLeft(this.$refs.barWrap)) / barWidth
|
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
|
|
||||||
this.$emit('dragbegin', percentage)
|
const emit = defineEmits(['dragbegin', 'dragging', 'dragend'])
|
||||||
document.addEventListener('mousemove', this.onDocumentMouseMove)
|
|
||||||
document.addEventListener('mouseup', this.onDocumentMouseUp)
|
|
||||||
},
|
|
||||||
onDocumentMouseMove (e) {
|
|
||||||
const barWidth = this.$refs.barWrap.clientWidth
|
|
||||||
let percentage = (e.clientX - getElementViewLeft(this.$refs.barWrap)) / barWidth
|
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
|
|
||||||
this.$emit('dragging', percentage)
|
const thumbHovered = ref(false)
|
||||||
},
|
const barWrap = ref<unknown>(null)
|
||||||
onDocumentMouseUp (e) {
|
|
||||||
document.removeEventListener('mouseup', this.onDocumentMouseUp)
|
|
||||||
document.removeEventListener('mousemove', this.onDocumentMouseMove)
|
|
||||||
|
|
||||||
const barWidth = this.$refs.barWrap.clientWidth
|
const onThumbMouseDown = (e: MouseEvent) => {
|
||||||
let percentage = (e.clientX - getElementViewLeft(this.$refs.barWrap)) / barWidth
|
const barWidth = (<HTMLElement>barWrap.value).clientWidth
|
||||||
percentage = percentage > 0 ? percentage : 0
|
let percentage = (e.clientX - getElementViewLeft(<HTMLElement>barWrap.value)) / barWidth
|
||||||
percentage = percentage < 1 ? percentage : 1
|
percentage = percentage > 0 ? percentage : 0
|
||||||
this.$emit('dragend', percentage)
|
percentage = percentage < 1 ? percentage : 1
|
||||||
},
|
emit('dragbegin', percentage)
|
||||||
onThumbTouchStart (e) {
|
document.addEventListener('mousemove', onDocumentMouseMove)
|
||||||
const barWidth = this.$refs.barWrap.clientWidth
|
document.addEventListener('mouseup', onDocumentMouseUp)
|
||||||
let percentage = (e.clientX - getElementViewLeft(this.$refs.barWrap)) / barWidth
|
}
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
|
|
||||||
this.$emit('dragbegin', percentage)
|
const onDocumentMouseMove = (e: MouseEvent) => {
|
||||||
document.addEventListener('touchmove', this.onDocumentTouchMove)
|
const barWidth = (<HTMLElement>barWrap.value).clientWidth
|
||||||
document.addEventListener('touchend', this.onDocumentTouchEnd)
|
let percentage = (e.clientX - getElementViewLeft(<HTMLElement>barWrap.value)) / barWidth
|
||||||
},
|
percentage = percentage > 0 ? percentage : 0
|
||||||
onDocumentTouchMove (e) {
|
percentage = percentage < 1 ? percentage : 1
|
||||||
const touch = e.changedTouches[0]
|
emit('dragging', percentage)
|
||||||
const barWidth = this.$refs.barWrap.clientWidth
|
}
|
||||||
let percentage = (touch.clientX - getElementViewLeft(this.$refs.barWrap)) / barWidth
|
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
|
|
||||||
this.$emit('dragging', percentage)
|
const onDocumentMouseUp = (e: MouseEvent) => {
|
||||||
},
|
document.removeEventListener('mouseup', onDocumentMouseUp)
|
||||||
onDocumentTouchEnd (e) {
|
document.removeEventListener('mousemove', onDocumentMouseMove)
|
||||||
document.removeEventListener('touchend', this.onDocumentTouchEnd)
|
const barWidth = (<HTMLElement>barWrap.value).clientWidth
|
||||||
document.removeEventListener('touchmove', this.onDocumentTouchMove)
|
let percentage = (e.clientX - getElementViewLeft(<HTMLElement>barWrap.value)) / barWidth
|
||||||
|
percentage = percentage > 0 ? percentage : 0
|
||||||
const touch = e.changedTouches[0]
|
percentage = percentage < 1 ? percentage : 1
|
||||||
const barWidth = this.$refs.barWrap.clientWidth
|
emit('dragend', percentage)
|
||||||
let percentage = (touch.clientX - getElementViewLeft(this.$refs.barWrap)) / barWidth
|
}
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
this.$emit('dragend', percentage)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.aplayer-bar-wrap {
|
||||||
.aplayer-bar-wrap {
|
margin: 0 0 0 5px;
|
||||||
margin: 0 0 0 5px;
|
padding: 4px 0;
|
||||||
padding: 4px 0;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
flex: 1;
|
||||||
flex: 1;
|
.aplayer-bar {
|
||||||
|
position: relative;
|
||||||
.aplayer-bar {
|
height: 2px;
|
||||||
position: relative;
|
width: 100%;
|
||||||
|
background: #cdcdcd;
|
||||||
|
.aplayer-loaded {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #aaa;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
width: 100%;
|
transition: all 0.5s ease;
|
||||||
background: #cdcdcd;
|
will-change: width;
|
||||||
|
}
|
||||||
.aplayer-loaded {
|
.aplayer-played {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 2px;
|
||||||
|
transition: background-color .3s;
|
||||||
|
will-change: width;
|
||||||
|
.aplayer-thumb {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
right: 5px;
|
||||||
background: #aaa;
|
margin-top: -5px;
|
||||||
height: 2px;
|
margin-right: -10px;
|
||||||
transition: all 0.5s ease;
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
will-change: width;
|
border: 1px solid;
|
||||||
}
|
transform: scale(.8);
|
||||||
|
will-change: transform;
|
||||||
.aplayer-played {
|
transition: transform 300ms, background-color .3s, border-color .3s;
|
||||||
position: absolute;
|
border-radius: 50%;
|
||||||
left: 0;
|
background: #fff;
|
||||||
top: 0;
|
cursor: pointer;
|
||||||
bottom: 0;
|
&:hover {
|
||||||
height: 2px;
|
transform: scale(1);
|
||||||
transition: background-color .3s;
|
}
|
||||||
will-change: width;
|
overflow: hidden;
|
||||||
|
.aplayer-loading-icon {
|
||||||
.aplayer-thumb {
|
display: none;
|
||||||
position: absolute;
|
width: 100%;
|
||||||
top: 0;
|
height: 100%;
|
||||||
right: 5px;
|
.icon-svg {
|
||||||
margin-top: -5px;
|
position: absolute;
|
||||||
margin-right: -10px;
|
animation: spin 1s linear infinite;
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border: 1px solid;
|
|
||||||
transform: scale(.8);
|
|
||||||
will-change: transform;
|
|
||||||
transition: transform 300ms, background-color .3s, border-color .3s;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
.aplayer-loading-icon {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.icon-svg {
|
|
||||||
position: absolute;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.aplayer-loading {
|
.aplayer-loading {
|
||||||
.aplayer-bar-wrap .aplayer-bar .aplayer-thumb .aplayer-loading-icon {
|
.aplayer-bar-wrap .aplayer-bar .aplayer-thumb .aplayer-loading-icon {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
|
||||||
@keyframes spin {
|
transform: scale(1);
|
||||||
0% {
|
|
||||||
transform: rotate(0)
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0)
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg)
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -23,106 +23,95 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import IconButton from './aplayer-iconbutton.vue'
|
import { computed, ref } from 'vue'
|
||||||
import {getElementViewTop} from '../utils'
|
import IconButton from './aplayer-iconbutton.vue'
|
||||||
|
import {getElementViewTop} from '../utils'
|
||||||
|
|
||||||
const barHeight = 40
|
const props = defineProps<{
|
||||||
|
volume: number,
|
||||||
|
muted: boolean,
|
||||||
|
theme: string
|
||||||
|
}>()
|
||||||
|
const emit = defineEmits(['setvolume'])
|
||||||
|
const barHeight = 40
|
||||||
|
|
||||||
export default {
|
const volumeIcon = computed(() => {
|
||||||
components: {
|
if (props.muted || props.volume <= 0) return 'volume_off'
|
||||||
IconButton,
|
if (props.volume >= 1) return 'volume_up'
|
||||||
},
|
return 'volume_down'
|
||||||
props: ['volume', 'muted', 'theme'],
|
})
|
||||||
computed: {
|
|
||||||
volumeIcon () {
|
|
||||||
if (this.muted || this.volume <= 0) return 'volume_off'
|
|
||||||
if (this.volume >= 1) return 'volume_up'
|
|
||||||
return 'volume_down'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
adjustVolume (e) {
|
|
||||||
let percentage = (barHeight - e.clientY + getElementViewTop(this.$refs.bar)) / barHeight
|
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
this.$emit('setvolume', percentage)
|
|
||||||
},
|
|
||||||
onBarMouseDown () {
|
|
||||||
document.addEventListener('mousemove', this.onDocumentMouseMove)
|
|
||||||
document.addEventListener('mouseup', this.onDocumentMouseUp)
|
|
||||||
},
|
|
||||||
onDocumentMouseMove (e) {
|
|
||||||
let percentage = (barHeight - e.clientY + getElementViewTop(this.$refs.bar)) / barHeight
|
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
|
||||||
this.$emit('setvolume', percentage)
|
|
||||||
},
|
|
||||||
onDocumentMouseUp (e) {
|
|
||||||
document.removeEventListener('mouseup', this.onDocumentMouseUp)
|
|
||||||
document.removeEventListener('mousemove', this.onDocumentMouseMove)
|
|
||||||
|
|
||||||
let percentage = (barHeight - e.clientY + getElementViewTop(this.$refs.bar)) / barHeight
|
const bar = ref<unknown>(null)
|
||||||
percentage = percentage > 0 ? percentage : 0
|
|
||||||
percentage = percentage < 1 ? percentage : 1
|
const onBarMouseDown = () => {
|
||||||
this.$emit('setvolume', percentage)
|
document.addEventListener('mousemove', onDocumentMouseMove)
|
||||||
}
|
document.addEventListener('mouseup', onDocumentMouseUp)
|
||||||
}
|
}
|
||||||
}
|
const onDocumentMouseMove = (e: MouseEvent) => {
|
||||||
|
let percentage = (barHeight - e.clientY + getElementViewTop(<HTMLElement>bar.value)) / barHeight
|
||||||
|
percentage = percentage > 0 ? percentage : 0
|
||||||
|
percentage = percentage < 1 ? percentage : 1
|
||||||
|
emit('setvolume', percentage)
|
||||||
|
}
|
||||||
|
const onDocumentMouseUp = (e: MouseEvent) => {
|
||||||
|
document.removeEventListener('mouseup', onDocumentMouseUp)
|
||||||
|
document.removeEventListener('mousemove', onDocumentMouseMove)
|
||||||
|
|
||||||
|
let percentage = (barHeight - e.clientY + getElementViewTop(<HTMLElement>bar.value)) / barHeight
|
||||||
|
percentage = percentage > 0 ? percentage : 0
|
||||||
|
percentage = percentage < 1 ? percentage : 1
|
||||||
|
emit('setvolume', percentage)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.aplayer-volume-wrap {
|
||||||
.aplayer-volume-wrap {
|
position: relative;
|
||||||
position: relative;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
z-index: 0;
|
||||||
z-index: 0;
|
&:hover .aplayer-volume-bar-wrap {
|
||||||
|
display: block;
|
||||||
&:hover .aplayer-volume-bar-wrap {
|
}
|
||||||
display: block;
|
.aplayer-volume-bar-wrap {
|
||||||
}
|
display: none;
|
||||||
|
position: absolute;
|
||||||
.aplayer-volume-bar-wrap {
|
bottom: 15px;
|
||||||
display: none;
|
left: -4px;
|
||||||
|
right: -4px;
|
||||||
|
height: 40px;
|
||||||
|
z-index: -1;
|
||||||
|
transition: all .2s ease;
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 15px;
|
bottom: -16px;
|
||||||
left: -4px;
|
left: 0;
|
||||||
right: -4px;
|
right: 0;
|
||||||
|
height: 62px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.07), 0 0 5px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.aplayer-volume-bar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 11px;
|
||||||
|
width: 5px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
z-index: -1;
|
background: #aaa;
|
||||||
transition: all .2s ease;
|
border-radius: 2.5px;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
&::after {
|
.aplayer-volume {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -16px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 62px;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.07), 0 0 5px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-volume-bar {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 11px;
|
left: 0;
|
||||||
width: 5px;
|
right: 0;
|
||||||
height: 40px;
|
transition: height 0.1s ease, background-color .3s;
|
||||||
background: #aaa;
|
will-change: height;
|
||||||
border-radius: 2.5px;
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.aplayer-volume {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
transition: height 0.1s ease, background-color .3s;
|
|
||||||
will-change: height;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -14,12 +14,12 @@
|
|||||||
class="aplayer-dtime">{{secondToTime(stat.duration)}}</span>
|
class="aplayer-dtime">{{secondToTime(stat.duration)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<volume
|
<volume
|
||||||
v-if="!$parent.isMobile"
|
v-if="!isMobile"
|
||||||
:volume="volume"
|
:volume="volume"
|
||||||
:theme="theme"
|
:theme="theme"
|
||||||
:muted="muted"
|
:muted="muted"
|
||||||
@togglemute="$emit('togglemute')"
|
@togglemute="$emit('togglemute')"
|
||||||
@setvolume="v => $emit('setvolume', v)"
|
@setvolume="(v: number) => $emit('setvolume', v)"
|
||||||
/>
|
/>
|
||||||
<icon-button
|
<icon-button
|
||||||
class="aplayer-icon-mode"
|
class="aplayer-icon-mode"
|
||||||
@ -36,112 +36,94 @@
|
|||||||
<icon-button
|
<icon-button
|
||||||
class="aplayer-icon-menu"
|
class="aplayer-icon-menu"
|
||||||
icon="menu"
|
icon="menu"
|
||||||
:class="{ 'inactive': !$parent.showList }"
|
:class="{ 'inactive': !showList }"
|
||||||
@click="$emit('togglelist')"
|
@click="$emit('togglelist')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import IconButton from './aplayer-iconbutton.vue'
|
import { computed } from 'vue'
|
||||||
import VProgress from './aplayer-controller-progress.vue'
|
import IconButton from './aplayer-iconbutton.vue'
|
||||||
import Volume from './aplayer-controller-volume.vue'
|
import VProgress from './aplayer-controller-progress.vue'
|
||||||
|
import Volume from './aplayer-controller-volume.vue'
|
||||||
|
import { StatType } from '@/model/api/music'
|
||||||
|
|
||||||
export default {
|
const props = defineProps<{
|
||||||
components: {
|
shuffle: boolean,
|
||||||
IconButton,
|
repeat: string,
|
||||||
VProgress,
|
stat: StatType,
|
||||||
Volume,
|
theme: string
|
||||||
},
|
volume: number,
|
||||||
props: ['shuffle', 'repeat', 'stat', 'theme', 'volume', 'muted'],
|
muted: boolean,
|
||||||
computed: {
|
showList: boolean,
|
||||||
loadProgress () {
|
isMobile: boolean
|
||||||
if (this.stat.duration === 0) return 0
|
}>()
|
||||||
return this.stat.loadedTime / this.stat.duration
|
const loadProgress = computed(() => {
|
||||||
},
|
if (props.stat.duration === 0) return 0
|
||||||
playProgress () {
|
return props.stat.loadedTime / props.stat.duration
|
||||||
if (this.stat.duration === 0) return 0
|
})
|
||||||
return this.stat.playedTime / this.stat.duration
|
const playProgress = computed(() => {
|
||||||
},
|
if (props.stat.duration === 0) return 0
|
||||||
},
|
return props.stat.playedTime / props.stat.duration
|
||||||
methods: {
|
})
|
||||||
secondToTime (second) {
|
|
||||||
if (isNaN(second)) {
|
|
||||||
return '00:00'
|
|
||||||
}
|
|
||||||
const pad0 = (num) => {
|
|
||||||
return num < 10 ? '0' + num : '' + num
|
|
||||||
}
|
|
||||||
|
|
||||||
const min = Math.trunc(second / 60)
|
const secondToTime = (second: number) => {
|
||||||
const sec = Math.trunc(second - min * 60)
|
if (isNaN(second)) {
|
||||||
const hours = Math.trunc(min / 60)
|
return '00:00'
|
||||||
const minAdjust = Math.trunc((second / 60) - (60 * Math.trunc((second / 60) / 60)))
|
|
||||||
return second >= 3600 ? pad0(hours) + ':' + pad0(minAdjust) + ':' + pad0(sec) : pad0(min) + ':' + pad0(sec)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
const pad0 = (num: number) => {
|
||||||
|
return num < 10 ? '0' + num : '' + num
|
||||||
|
}
|
||||||
|
|
||||||
|
const min = Math.trunc(second / 60)
|
||||||
|
const sec = Math.trunc(second - min * 60)
|
||||||
|
const hours = Math.trunc(min / 60)
|
||||||
|
const minAdjust = Math.trunc((second / 60) - (60 * Math.trunc((second / 60) / 60)))
|
||||||
|
return second >= 3600 ? pad0(hours) + ':' + pad0(minAdjust) + ':' + pad0(sec) : pad0(min) + ':' + pad0(sec)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.aplayer-controller {
|
||||||
.aplayer-controller {
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
.aplayer-time {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: 17px;
|
||||||
.aplayer-time {
|
color: #999;
|
||||||
display: flex;
|
font-size: 11px;
|
||||||
align-items: center;
|
padding-left: 7px;
|
||||||
position: relative;
|
.aplayer-volume-wrap {
|
||||||
height: 17px;
|
margin-left: 4px;
|
||||||
color: #999;
|
margin-right: 4px;
|
||||||
font-size: 11px;
|
}
|
||||||
padding-left: 7px;
|
.aplayer-icon {
|
||||||
|
cursor: pointer;
|
||||||
.aplayer-volume-wrap {
|
transition: all 0.2s ease;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-right: 4px;
|
&.inactive {
|
||||||
|
opacity: .3;
|
||||||
}
|
}
|
||||||
|
&.aplayer-icon-menu {
|
||||||
.aplayer-icon {
|
display: none;
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
margin-left: 4px;
|
|
||||||
|
|
||||||
&.inactive {
|
|
||||||
opacity: .3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-fill {
|
|
||||||
fill: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.aplayer-fill {
|
|
||||||
fill: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.aplayer-icon-menu {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.aplayer-volume-wrap + .aplayer-icon {
|
}
|
||||||
margin-left: 0;
|
.aplayer-volume-wrap + .aplayer-icon {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
&.aplayer-time-narrow {
|
||||||
|
.aplayer-icon-mode {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
.aplayer-icon-menu {
|
||||||
&.aplayer-time-narrow {
|
display: none;
|
||||||
.aplayer-icon-mode {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-icon-menu {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -2,26 +2,23 @@
|
|||||||
<img class="icon-svg" :src="svg" :style="style" />
|
<img class="icon-svg" :src="svg" :style="style" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
const requireAssets = require.context('../assets', false, /\.svg$/)
|
import { computed } from 'vue'
|
||||||
const SVGs = requireAssets.keys().reduce((svgs, path) => {
|
const props = defineProps<{type: string}>()
|
||||||
const svgFile = requireAssets(path)
|
const requireAssets = require.context('../assets', false, /\.svg$/)
|
||||||
svgs[path.match(/^.*\/(.+?)\.svg$/)[1]] = svgFile
|
const SVGs = requireAssets.keys().reduce((svgs: {[propName:string]: string}, path: string) => {
|
||||||
return svgs
|
const svgFile = requireAssets(path)
|
||||||
}, {})
|
const fileNameMatch = path.match(/^.*\/(.+?)\.svg$/)
|
||||||
export default {
|
if (fileNameMatch) {
|
||||||
props: ['type'],
|
svgs[fileNameMatch[1]] = svgFile
|
||||||
computed: {
|
|
||||||
svg () {
|
|
||||||
return SVGs[this.type] || {}
|
|
||||||
},
|
|
||||||
style () {
|
|
||||||
if (this.type === 'next') {
|
|
||||||
return {
|
|
||||||
transform: 'rotate(180deg)',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return svgs
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
const svg = computed(() => SVGs[props.type])
|
||||||
|
const style = computed(() => {
|
||||||
|
if (props.type === 'next') {
|
||||||
|
return { transform: 'rotate(180deg)' }
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
@ -3,43 +3,34 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="aplayer-icon"
|
class="aplayer-icon"
|
||||||
>
|
>
|
||||||
<icon :type="icon"/>
|
<player-icon :type="icon"/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import Icon from './aplayer-icon.vue'
|
import PlayerIcon from './aplayer-icon.vue'
|
||||||
|
defineProps<{icon: string}>()
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Icon,
|
|
||||||
},
|
|
||||||
props: ['icon'],
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.aplayer-icon {
|
.aplayer-icon {
|
||||||
width: 15px;
|
width: 15px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
outline: none;
|
outline: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: .8;
|
opacity: .8;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: inline;
|
display: inline;
|
||||||
|
&:hover {
|
||||||
&:hover {
|
opacity: 1;
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-fill {
|
|
||||||
transition: all .2s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.aplayer-fill {
|
||||||
|
transition: all .2s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -6,10 +6,7 @@
|
|||||||
ref="list"
|
ref="list"
|
||||||
v-show="show"
|
v-show="show"
|
||||||
>
|
>
|
||||||
<ol
|
<ol ref="ol" :style="listHeightStyle">
|
||||||
ref="ol"
|
|
||||||
:style="listHeightStyle"
|
|
||||||
>
|
|
||||||
<li
|
<li
|
||||||
v-for="(aMusic, index) of musicList"
|
v-for="(aMusic, index) of musicList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -19,148 +16,120 @@
|
|||||||
<span class="aplayer-list-cur" :style="{background: theme}"></span>
|
<span class="aplayer-list-cur" :style="{background: theme}"></span>
|
||||||
<span class="aplayer-list-index">{{ index + 1}}</span>
|
<span class="aplayer-list-index">{{ index + 1}}</span>
|
||||||
<span class="aplayer-list-title">{{ aMusic.title || 'Untitled' }}</span>
|
<span class="aplayer-list-title">{{ aMusic.title || 'Untitled' }}</span>
|
||||||
<span class="aplayer-list-author">{{ aMusic.artist || aMusic.author || 'Unknown' }}</span>
|
<span class="aplayer-list-author">{{ aMusic.artist || 'Unknown' }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
export default {
|
import { computed } from 'vue'
|
||||||
props: {
|
import { MusicPlayerItem } from '@/model/api/music'
|
||||||
show: {
|
|
||||||
type: Boolean,
|
const props = defineProps<{
|
||||||
default: true,
|
show: boolean,
|
||||||
},
|
currentMusic: MusicPlayerItem,
|
||||||
currentMusic: Object,
|
musicList: MusicPlayerItem[],
|
||||||
musicList: {
|
theme?: string,
|
||||||
type: Array,
|
listMaxHeight?: string
|
||||||
default () {
|
}>()
|
||||||
return []
|
const listHeightStyle = computed(() => {
|
||||||
}
|
return {
|
||||||
},
|
height: `${33 * props.musicList.length - 1}px`,
|
||||||
playIndex: {
|
maxHeight: props.listMaxHeight || ''
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
theme: String,
|
|
||||||
listMaxHeight: String,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
listHeightStyle () {
|
|
||||||
return {
|
|
||||||
height: `${33 * this.musicList.length - 1}px`,
|
|
||||||
maxHeight: this.listMaxHeight || ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.aplayer-list {
|
||||||
.aplayer-list {
|
overflow: hidden;
|
||||||
overflow: hidden;
|
&.slide-v-enter-active,
|
||||||
|
&.slide-v-leave-active {
|
||||||
&.slide-v-enter-active,
|
transition: height 500ms ease;
|
||||||
&.slide-v-leave-active {
|
will-change: height;
|
||||||
transition: height 500ms ease;
|
}
|
||||||
will-change: height;
|
&.slide-v-enter,
|
||||||
|
&.slide-v-leave-to {
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
ol {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
}
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
&.slide-v-enter,
|
background-color: #f9f9f9;
|
||||||
&.slide-v-leave-to {
|
|
||||||
height: 0 !important;
|
|
||||||
}
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
ol {
|
border-radius: 3px;
|
||||||
list-style-type: none;
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
li.aplayer-list-light:not(:hover) {
|
||||||
|
background-color: inherit;
|
||||||
|
transition: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(:hover) {
|
||||||
|
li.aplayer-list-light {
|
||||||
|
transition: background-color .6s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
padding: 0 15px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-top: 1px solid #e9e9e9;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
overflow: hidden;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
text-align: start;
|
||||||
overflow-y: auto;
|
display: flex;
|
||||||
|
&:first-child {
|
||||||
&::-webkit-scrollbar {
|
border-top: none;
|
||||||
width: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
li.aplayer-list-light:not(:hover) {
|
background: #efefef;
|
||||||
background-color: inherit;
|
|
||||||
transition: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
&.aplayer-list-light {
|
||||||
&:not(:hover) {
|
background: #efefef;
|
||||||
li.aplayer-list-light {
|
|
||||||
transition: background-color .6s ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
position: relative;
|
|
||||||
height: 32px;
|
|
||||||
line-height: 32px;
|
|
||||||
padding: 0 15px;
|
|
||||||
font-size: 12px;
|
|
||||||
border-top: 1px solid #e9e9e9;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 0;
|
|
||||||
text-align: start;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #efefef;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.aplayer-list-light {
|
|
||||||
background: #efefef;
|
|
||||||
|
|
||||||
.aplayer-list-cur {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-list-cur {
|
.aplayer-list-cur {
|
||||||
display: none;
|
display: inline-block;
|
||||||
width: 3px;
|
|
||||||
height: 22px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 5px;
|
|
||||||
transition: background-color .3s;
|
|
||||||
}
|
|
||||||
.aplayer-list-index {
|
|
||||||
color: #666;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
.aplayer-list-title {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
.aplayer-list-author {
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: #666;
|
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.aplayer-list-cur {
|
||||||
|
display: none;
|
||||||
|
width: 3px;
|
||||||
|
height: 22px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 5px;
|
||||||
|
transition: background-color .3s;
|
||||||
|
}
|
||||||
|
.aplayer-list-index {
|
||||||
|
color: #666;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
.aplayer-list-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.aplayer-list-author {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #666;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -15,150 +15,114 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import {parseLrc} from '../utils'
|
import { computed, watch, ref } from 'vue'
|
||||||
|
import { parseLrc } from '../utils'
|
||||||
|
import { MusicPlayerItem, StatType } from '@/model/api/music'
|
||||||
|
|
||||||
export default {
|
const props = defineProps<{
|
||||||
props: {
|
currentMusic: MusicPlayerItem,
|
||||||
currentMusic: {
|
playStat: StatType
|
||||||
type: Object,
|
}>()
|
||||||
required: true
|
|
||||||
},
|
const displayLrc = ref('')
|
||||||
playStat: {
|
const currentLineIndex = ref(0)
|
||||||
type: Object,
|
|
||||||
required: true
|
const lrcLines = computed(() => parseLrc(displayLrc.value))
|
||||||
}
|
|
||||||
},
|
const transformStyle = computed(() => {
|
||||||
data () {
|
return {
|
||||||
return {
|
transform: `translateY(${-currentLineIndex.value * 16}px)`,
|
||||||
displayLrc: '',
|
webkitTransform: `translateY(${-currentLineIndex.value * 16}px)`,
|
||||||
currentLineIndex: 0,
|
}
|
||||||
}
|
})
|
||||||
},
|
|
||||||
computed: {
|
const applyLrc = (lrc: string) => {
|
||||||
lrcLines () {
|
if (/^https?:\/\//.test(lrc)) {
|
||||||
return parseLrc(this.displayLrc)
|
fetch(lrc)
|
||||||
},
|
.then(response => response.text())
|
||||||
currentLine () {
|
.then((lrc) => displayLrc.value = lrc)
|
||||||
if (this.currentLineIndex > this.lrcLines.length - 1) {
|
} else {
|
||||||
return null
|
displayLrc.value = lrc
|
||||||
}
|
}
|
||||||
return this.lrcLines[this.currentLineIndex]
|
}
|
||||||
},
|
|
||||||
transformStyle () {
|
watch(props.currentMusic, (music: MusicPlayerItem) => {
|
||||||
// transform: translateY(0); -webkit-transform: translateY(0);
|
currentLineIndex.value = 0
|
||||||
return {
|
if (music.lrc) {
|
||||||
transform: `translateY(${-this.currentLineIndex * 16}px)`,
|
applyLrc(music.lrc)
|
||||||
webkitTransform: `translateY(${-this.currentLineIndex * 16}px)`,
|
} else {
|
||||||
}
|
displayLrc.value = ''
|
||||||
},
|
}
|
||||||
},
|
}, { immediate: true })
|
||||||
methods: {
|
|
||||||
applyLrc (lrc) {
|
watch(() => props.playStat.playedTime, (playedTime: number) => {
|
||||||
if (/^https?:\/\//.test(lrc)) {
|
for (let i = 0; i < lrcLines.value.length; i++) {
|
||||||
this.fetchLrc(lrc)
|
const line = lrcLines.value[i]
|
||||||
} else {
|
const nextLine = lrcLines.value[i + 1]
|
||||||
this.displayLrc = lrc
|
if (playedTime >= line[0] && (!nextLine || playedTime < nextLine[0])) {
|
||||||
}
|
currentLineIndex.value = i
|
||||||
},
|
|
||||||
fetchLrc (src) {
|
|
||||||
fetch(src)
|
|
||||||
.then(response => response.text())
|
|
||||||
.then((lrc) => {
|
|
||||||
this.displayLrc = lrc
|
|
||||||
})
|
|
||||||
},
|
|
||||||
hideLrc () {
|
|
||||||
this.displayLrc = ''
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
currentMusic: {
|
|
||||||
immediate: true,
|
|
||||||
handler (music) {
|
|
||||||
this.currentLineIndex = 0
|
|
||||||
if (music.lrc) {
|
|
||||||
this.applyLrc(music.lrc)
|
|
||||||
} else {
|
|
||||||
this.hideLrc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'playStat.playedTime' (playedTime) {
|
|
||||||
for (let i = 0; i < this.lrcLines.length; i++) {
|
|
||||||
const line = this.lrcLines[i]
|
|
||||||
const nextLine = this.lrcLines[i + 1]
|
|
||||||
if (playedTime >= line[0] && (!nextLine || playedTime < nextLine[0])) {
|
|
||||||
this.currentLineIndex = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@import "../less/variables";
|
@import "../less/variables";
|
||||||
|
.aplayer-lrc {
|
||||||
.aplayer-lrc {
|
position: relative;
|
||||||
position: relative;
|
height: @lrc-height;
|
||||||
height: @lrc-height;
|
text-align: center;
|
||||||
text-align: center;
|
overflow: hidden;
|
||||||
|
margin-bottom: 7px;
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 7px;
|
width: 100%;
|
||||||
|
height: 10%;
|
||||||
&:before {
|
content: ' ';
|
||||||
position: absolute;
|
background: -moz-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
||||||
top: 0;
|
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
||||||
z-index: 1;
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
||||||
display: block;
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#00ffffff', GradientType=0);
|
||||||
overflow: hidden;
|
}
|
||||||
width: 100%;
|
&:after {
|
||||||
height: 10%;
|
position: absolute;
|
||||||
content: ' ';
|
bottom: 0;
|
||||||
background: -moz-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
z-index: 1;
|
||||||
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
display: block;
|
||||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
|
overflow: hidden;
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#00ffffff', GradientType=0);
|
width: 100%;
|
||||||
}
|
height: 33%;
|
||||||
|
content: ' ';
|
||||||
&:after {
|
background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
||||||
position: absolute;
|
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
||||||
bottom: 0;
|
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
||||||
z-index: 1;
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffffff', endColorstr='#ccffffff', GradientType=0);
|
||||||
display: block;
|
}
|
||||||
overflow: hidden;
|
p {
|
||||||
width: 100%;
|
font-size: 12px;
|
||||||
height: 33%;
|
color: #666;
|
||||||
content: ' ';
|
line-height: 16px;
|
||||||
background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
height: 16px;
|
||||||
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
padding: 0;
|
||||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.8) 100%);
|
margin: 0;
|
||||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffffff', endColorstr='#ccffffff', GradientType=0);
|
transition: all 0.5s ease-out;
|
||||||
}
|
opacity: 0.4;
|
||||||
|
overflow: hidden;
|
||||||
p {
|
&.aplayer-lrc-current {
|
||||||
font-size: 12px;
|
opacity: 1;
|
||||||
color: #666;
|
overflow: visible;
|
||||||
line-height: 16px;
|
height: initial;
|
||||||
height: 16px;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
transition: all 0.5s ease-out;
|
|
||||||
opacity: 0.4;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&.aplayer-lrc-current {
|
|
||||||
opacity: 1;
|
|
||||||
overflow: visible;
|
|
||||||
height: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-lrc-contents {
|
|
||||||
width: 100%;
|
|
||||||
transition: all 0.5s ease-out;
|
|
||||||
user-select: text;
|
|
||||||
cursor: default;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.aplayer-lrc-contents {
|
||||||
|
width: 100%;
|
||||||
|
transition: all 0.5s ease-out;
|
||||||
|
user-select: text;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -13,139 +13,121 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
import IconButton from './aplayer-iconbutton.vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import IconButton from './aplayer-iconbutton.vue'
|
||||||
|
|
||||||
export default {
|
const props = withDefaults(defineProps<{
|
||||||
components: {
|
pic?: string,
|
||||||
IconButton,
|
theme?: string,
|
||||||
},
|
playing: boolean
|
||||||
props: {
|
enableDrag: boolean
|
||||||
pic: String,
|
}>(), {
|
||||||
theme: String,
|
playing: false,
|
||||||
playing: {
|
enableDrag: false
|
||||||
type: Boolean,
|
})
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
enableDrag: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
hasMovedSinceMouseDown: false,
|
|
||||||
dragStartX: 0,
|
|
||||||
dragStartY: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
currentPicStyleObj () {
|
|
||||||
if (!this.pic) return {}
|
|
||||||
return {
|
|
||||||
backgroundImage: `url(${this.pic})`,
|
|
||||||
backgroundColor: this.theme
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onDragBegin (e) {
|
|
||||||
if (this.enableDrag) {
|
|
||||||
this.hasMovedSinceMouseDown = false
|
|
||||||
this.$emit('dragbegin')
|
|
||||||
this.dragStartX = e.clientX
|
|
||||||
this.dragStartY = e.clientY
|
|
||||||
document.addEventListener('mousemove', this.onDocumentMouseMove)
|
|
||||||
document.addEventListener('mouseup', this.onDocumentMouseUp)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDocumentMouseMove (e) {
|
|
||||||
this.hasMovedSinceMouseDown = true
|
|
||||||
this.$emit('dragging', {offsetLeft: e.clientX - this.dragStartX, offsetTop: e.clientY - this.dragStartY})
|
|
||||||
},
|
|
||||||
onDocumentMouseUp (e) {
|
|
||||||
document.removeEventListener('mouseup', this.onDocumentMouseUp)
|
|
||||||
document.removeEventListener('mousemove', this.onDocumentMouseMove)
|
|
||||||
|
|
||||||
this.$emit('dragend')
|
const emit = defineEmits(['dragbegin', 'dragging', 'dragend', 'toggleplay'])
|
||||||
},
|
|
||||||
onClick () {
|
const hasMovedSinceMouseDown = ref(false)
|
||||||
if (!this.hasMovedSinceMouseDown) {
|
const dragStartX = ref(0)
|
||||||
this.$emit('toggleplay')
|
const dragStartY = ref(0)
|
||||||
}
|
|
||||||
}
|
const currentPicStyleObj = computed(() => {
|
||||||
}
|
if (!props.pic) return {}
|
||||||
|
return {
|
||||||
|
backgroundImage: `url(${props.pic})`,
|
||||||
|
backgroundColor: props.theme
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onDragBegin = (e: MouseEvent) => {
|
||||||
|
if (props.enableDrag) {
|
||||||
|
hasMovedSinceMouseDown.value = false
|
||||||
|
emit('dragbegin')
|
||||||
|
dragStartX.value = e.clientX
|
||||||
|
dragStartY.value = e.clientY
|
||||||
|
document.addEventListener('mousemove', onDocumentMouseMove)
|
||||||
|
document.addEventListener('mouseup', onDocumentMouseUp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onDocumentMouseMove = (e: MouseEvent) => {
|
||||||
|
hasMovedSinceMouseDown.value = true
|
||||||
|
emit('dragging', {offsetLeft: e.clientX - dragStartX.value, offsetTop: e.clientY - dragStartY.value})
|
||||||
|
}
|
||||||
|
const onDocumentMouseUp = () => {
|
||||||
|
document.removeEventListener('mouseup', onDocumentMouseUp)
|
||||||
|
document.removeEventListener('mousemove', onDocumentMouseMove)
|
||||||
|
emit('dragend')
|
||||||
|
}
|
||||||
|
const onClick = () => {
|
||||||
|
if (!hasMovedSinceMouseDown.value) {
|
||||||
|
emit('toggleplay')
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@import "../less/variables";
|
@import "../less/variables";
|
||||||
|
.aplayer-float {
|
||||||
.aplayer-float {
|
.aplayer-pic:active {
|
||||||
.aplayer-pic:active {
|
cursor: move;
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.aplayer-pic {
|
.aplayer-pic {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
position: relative;
|
height: @aplayer-height;
|
||||||
height: @aplayer-height;
|
width: @aplayer-height;
|
||||||
width: @aplayer-height;
|
background-image: url(../default.jpg);
|
||||||
background-image: url(../default.jpg);
|
background-size: cover;
|
||||||
background-size: cover;
|
transition: all 0.3s ease;
|
||||||
transition: all 0.3s ease;
|
cursor: pointer;
|
||||||
cursor: pointer;
|
&:hover {
|
||||||
&:hover {
|
|
||||||
.aplayer-button {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.aplayer-button {
|
.aplayer-button {
|
||||||
position: absolute;
|
opacity: 1;
|
||||||
border-radius: 50%;
|
|
||||||
opacity: 0.8;
|
|
||||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
|
||||||
transition: all 0.1s ease;
|
|
||||||
|
|
||||||
.aplayer-fill {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-play {
|
|
||||||
width: 26px;
|
|
||||||
height: 26px;
|
|
||||||
border: 2px solid #fff;
|
|
||||||
bottom: 50%;
|
|
||||||
right: 50%;
|
|
||||||
margin: 0 -15px -15px 0;
|
|
||||||
.aplayer-icon-play {
|
|
||||||
position: absolute;
|
|
||||||
top: 3px;
|
|
||||||
left: 4px;
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aplayer-pause {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border: 2px solid #fff;
|
|
||||||
bottom: 4px;
|
|
||||||
right: 4px;
|
|
||||||
.aplayer-icon-pause {
|
|
||||||
position: absolute;
|
|
||||||
top: 2px;
|
|
||||||
left: 2px;
|
|
||||||
height: 12px;
|
|
||||||
width: 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.aplayer-button {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 0.8;
|
||||||
|
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
transition: all 0.1s ease;
|
||||||
|
.aplayer-fill {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.aplayer-play {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
bottom: 50%;
|
||||||
|
right: 50%;
|
||||||
|
margin: 0 -15px -15px 0;
|
||||||
|
.aplayer-icon-play {
|
||||||
|
position: absolute;
|
||||||
|
top: 3px;
|
||||||
|
left: 4px;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.aplayer-pause {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
bottom: 4px;
|
||||||
|
right: 4px;
|
||||||
|
.aplayer-icon-pause {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,75 +0,0 @@
|
|||||||
/**
|
|
||||||
* Parse lrc, suppose multiple time tag
|
|
||||||
* @see https://github.com/MoePlayer/APlayer/blob/master/src/js/lrc.js#L83
|
|
||||||
* @author DIYgod(https://github.com/DIYgod)
|
|
||||||
*
|
|
||||||
* @param {String} lrc_s - Format:
|
|
||||||
* [mm:ss]lyric
|
|
||||||
* [mm:ss.xx]lyric
|
|
||||||
* [mm:ss.xxx]lyric
|
|
||||||
* [mm:ss.xx][mm:ss.xx][mm:ss.xx]lyric
|
|
||||||
* [mm:ss.xx]<mm:ss.xx>lyric
|
|
||||||
*
|
|
||||||
* @return {String} [[time, text], [time, text], [time, text], ...]
|
|
||||||
*/
|
|
||||||
export function parseLrc (lrc_s) {
|
|
||||||
if (lrc_s) {
|
|
||||||
lrc_s = lrc_s.replace(/([^\]^\n])\[/g, (match, p1) => p1 + '\n[')
|
|
||||||
const lyric = lrc_s.split('\n')
|
|
||||||
const lrc = []
|
|
||||||
const lyricLen = lyric.length
|
|
||||||
for (let i = 0; i < lyricLen; i++) {
|
|
||||||
// match lrc time
|
|
||||||
const lrcTimes = lyric[i].match(/\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g)
|
|
||||||
// match lrc text
|
|
||||||
const lrcText = lyric[i].replace(/.*\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g, '').replace(/<(\d{2}):(\d{2})(\.(\d{2,3}))?>/g, '').replace(/^\s+|\s+$/g, '')
|
|
||||||
|
|
||||||
if (lrcTimes) {
|
|
||||||
// handle multiple time tag
|
|
||||||
const timeLen = lrcTimes.length
|
|
||||||
for (let j = 0; j < timeLen; j++) {
|
|
||||||
const oneTime = /\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/.exec(lrcTimes[j])
|
|
||||||
const min2sec = oneTime[1] * 60
|
|
||||||
const sec2sec = parseInt(oneTime[2])
|
|
||||||
const msec2sec = oneTime[4] ? parseInt(oneTime[4]) / ((oneTime[4] + '').length === 2 ? 100 : 1000) : 0
|
|
||||||
const lrcTime = min2sec + sec2sec + msec2sec
|
|
||||||
lrc.push([lrcTime, lrcText])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// sort by time
|
|
||||||
lrc.sort((a, b) => a[0] - b[0])
|
|
||||||
return lrc
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function warn (message) {
|
|
||||||
return console.warn(`[Vue-APlayer] ${message}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getElementViewLeft (element) {
|
|
||||||
let actualLeft = element.offsetLeft
|
|
||||||
let current = element.offsetParent
|
|
||||||
let elementScrollLeft
|
|
||||||
while (current !== null) {
|
|
||||||
actualLeft += current.offsetLeft
|
|
||||||
current = current.offsetParent
|
|
||||||
}
|
|
||||||
elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft
|
|
||||||
return actualLeft - elementScrollLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getElementViewTop (element) {
|
|
||||||
let actualTop = element.offsetTop
|
|
||||||
let current = element.offsetParent
|
|
||||||
let elementScrollTop
|
|
||||||
while (current !== null) {
|
|
||||||
actualTop += current.offsetTop
|
|
||||||
current = current.offsetParent
|
|
||||||
}
|
|
||||||
elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop
|
|
||||||
return actualTop - elementScrollTop
|
|
||||||
}
|
|
||||||
69
src/views/api/aplayer/utils.ts
Normal file
69
src/views/api/aplayer/utils.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Parse lrc, suppose multiple time tag
|
||||||
|
* @param {string} lrc_s - Format:
|
||||||
|
* [mm:ss]lyric
|
||||||
|
* [mm:ss.xx]lyric
|
||||||
|
* [mm:ss.xxx]lyric
|
||||||
|
* [mm:ss.xx][mm:ss.xx][mm:ss.xx]lyric
|
||||||
|
* [mm:ss.xx]<mm:ss.xx>lyric
|
||||||
|
*
|
||||||
|
* @return {Array} [[time, text], [time, text], [time, text], ...]
|
||||||
|
*/
|
||||||
|
export function parseLrc (lrc_s: string): Array<[number, string]> {
|
||||||
|
if (!lrc_s) return []
|
||||||
|
lrc_s = lrc_s.replace(/([^\]^\n])\[/g, (match, p1) => p1 + '\n[')
|
||||||
|
const lyric = lrc_s.split('\n')
|
||||||
|
const lrc: Array<[number, string]> = []
|
||||||
|
const lyricLen = lyric.length
|
||||||
|
for (let i = 0; i < lyricLen; i++) {
|
||||||
|
// match lrc time
|
||||||
|
const lrcTimes = lyric[i].match(/\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g)
|
||||||
|
// match lrc text
|
||||||
|
const lrcText = lyric[i].replace(/.*\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g, '').replace(/<(\d{2}):(\d{2})(\.(\d{2,3}))?>/g, '').replace(/^\s+|\s+$/g, '')
|
||||||
|
|
||||||
|
if (lrcTimes) {
|
||||||
|
// handle multiple time tag
|
||||||
|
const timeLen = lrcTimes.length
|
||||||
|
for (let j = 0; j < timeLen; j++) {
|
||||||
|
const oneTime = /\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/.exec(lrcTimes[j])
|
||||||
|
if (!oneTime) continue
|
||||||
|
const min2sec = parseInt(oneTime[1]) * 60
|
||||||
|
const sec2sec = parseInt(oneTime[2])
|
||||||
|
const msec2sec = oneTime[4] ? parseInt(oneTime[4]) / ((oneTime[4] + '').length === 2 ? 100 : 1000) : 0
|
||||||
|
const lrcTime = min2sec + sec2sec + msec2sec
|
||||||
|
lrc.push([lrcTime, lrcText])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sort by time
|
||||||
|
lrc.sort((item1, item2) => item1[0] - item2[0])
|
||||||
|
return lrc
|
||||||
|
}
|
||||||
|
|
||||||
|
export function warn (message: string) {
|
||||||
|
return console.warn(`[Vue-APlayer] ${message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElementViewLeft (element: HTMLElement): number {
|
||||||
|
let actualLeft = element.offsetLeft
|
||||||
|
let current = <HTMLElement>element.offsetParent
|
||||||
|
let elementScrollLeft
|
||||||
|
while (current !== null) {
|
||||||
|
actualLeft += current.offsetLeft
|
||||||
|
current = <HTMLElement>current.offsetParent
|
||||||
|
}
|
||||||
|
elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft
|
||||||
|
return actualLeft - elementScrollLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElementViewTop (element: HTMLElement): number {
|
||||||
|
let actualTop = element.offsetTop
|
||||||
|
let current = <HTMLElement>element.offsetParent
|
||||||
|
let elementScrollTop
|
||||||
|
while (current !== null) {
|
||||||
|
actualTop += current.offsetTop
|
||||||
|
current = <HTMLElement>current.offsetParent
|
||||||
|
}
|
||||||
|
elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop
|
||||||
|
return actualTop - elementScrollTop
|
||||||
|
}
|
||||||
@ -35,6 +35,8 @@
|
|||||||
:volume="audioVolume"
|
:volume="audioVolume"
|
||||||
:muted="isAudioMuted"
|
:muted="isAudioMuted"
|
||||||
:theme="currentTheme"
|
:theme="currentTheme"
|
||||||
|
:showList="showList"
|
||||||
|
:isMobile="isMobile"
|
||||||
@toggleshuffle="shouldShuffle = !shouldShuffle"
|
@toggleshuffle="shouldShuffle = !shouldShuffle"
|
||||||
@togglelist="showList = !showList"
|
@togglelist="showList = !showList"
|
||||||
@togglemute="toggleMute"
|
@togglemute="toggleMute"
|
||||||
@ -490,7 +492,6 @@
|
|||||||
},
|
},
|
||||||
onProgressDragEnd (val) {
|
onProgressDragEnd (val) {
|
||||||
this.isSeeking = false
|
this.isSeeking = false
|
||||||
|
|
||||||
if (this.wasPlayingBeforeSeeking) {
|
if (this.wasPlayingBeforeSeeking) {
|
||||||
this.thenPlay()
|
this.thenPlay()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,11 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"lib": ["esnext", "dom"],
|
"lib": ["esnext", "dom"],
|
||||||
"allowJs": true
|
"allowJs": true,
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
const path = require('path')
|
||||||
const AutoImport = require('unplugin-auto-import/webpack')
|
const AutoImport = require('unplugin-auto-import/webpack')
|
||||||
const Components = require('unplugin-vue-components/webpack')
|
const Components = require('unplugin-vue-components/webpack')
|
||||||
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
|
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
|
||||||
@ -12,6 +13,9 @@ module.exports = defineConfig({
|
|||||||
transpileDependencies: true,
|
transpileDependencies: true,
|
||||||
productionSourceMap: false,
|
productionSourceMap: false,
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
|
resolve: {
|
||||||
|
alias: { '@': path.resolve(__dirname, './src') }
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
AutoImport({
|
AutoImport({
|
||||||
resolvers: [ElementPlusResolver()],
|
resolvers: [ElementPlusResolver()],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user