@meeovi/layer-lists 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/package.json +15 -3
- package/app/components/features/archived.vue +0 -64
- package/app/components/features/bookmarks.vue +0 -64
- package/app/components/features/lists.vue +0 -61
- package/app/components/features/starred.vue +0 -64
- package/app/components/lists/ListItemCard.vue +0 -190
- package/app/components/lists/add-bookmark.vue +0 -52
- package/app/components/lists/add-list-item.vue +0 -88
- package/app/components/lists/add-list.vue +0 -57
- package/app/components/lists/lists.vue +0 -6
- package/app/components/lists/listsettings.vue +0 -145
- package/app/components/lists/update-bookmark.vue +0 -267
- package/app/components/lists/update-list.vue +0 -192
- package/app/components/media/MediaPlayer.vue +0 -302
- package/app/components/partials/addtolist.vue +0 -233
- package/app/components/partials/createListBtn.vue +0 -95
- package/app/components/partials/listBtn.vue +0 -35
- package/app/components/related/list.vue +0 -33
- package/app/components/related/relatedlists.vue +0 -43
- package/app/components/tasks/TaskItem.vue +0 -204
- package/app/composables/bookmarks/createBookmark.js +0 -30
- package/app/composables/bookmarks/deleteBookmark.js +0 -15
- package/app/composables/bookmarks/updateBookmark.js +0 -15
- package/app/composables/config.ts +0 -17
- package/app/composables/content/uploadFiles.js +0 -41
- package/app/composables/globals/useDirectusForm.ts +0 -1
- package/app/composables/lists/createList.js +0 -25
- package/app/composables/lists/deleteList.js +0 -14
- package/app/composables/lists/updateList.js +0 -20
- package/app/composables/lists/useBookmarks.js +0 -69
- package/app/composables/lists/useLists.js +0 -120
- package/app/composables/lists/usePlaylist.js +0 -64
- package/app/composables/lists/useSaved.js +0 -29
- package/app/composables/lists/useTasks.js +0 -86
- package/app/composables/lists/useWishlist.js +0 -51
- package/app/composables/providers/atproto.ts +0 -156
- package/app/composables/providers/directus.ts +0 -49
- package/app/composables/providers/memory.ts +0 -88
- package/app/composables/registry.ts +0 -13
- package/app/composables/types.ts +0 -35
- package/app/composables/useLists.ts +0 -20
- package/app/composables/utils/transforms.ts +0 -42
- package/app/composables/utils/validation.ts +0 -21
- package/app/pages/lists/bookmark/[id].vue +0 -76
- package/app/pages/lists/index.vue +0 -152
- package/app/pages/lists/list/[...slug].vue +0 -233
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div>
|
|
3
|
-
<v-row justify="center">
|
|
4
|
-
<v-card>
|
|
5
|
-
<form @submit.prevent="handleSubmit">
|
|
6
|
-
<v-toolbar dark color="primary">
|
|
7
|
-
<v-btn icon dark @click="dialog = false">
|
|
8
|
-
<v-icon icon="fas:fa fa-circle-xmark"></v-icon>
|
|
9
|
-
</v-btn>
|
|
10
|
-
<v-card-title>
|
|
11
|
-
<span class="text-h6">Create a new Space</span>
|
|
12
|
-
</v-card-title>
|
|
13
|
-
</v-toolbar>
|
|
14
|
-
<v-card-text>
|
|
15
|
-
<v-container>
|
|
16
|
-
<v-row>
|
|
17
|
-
<v-col cols="12">
|
|
18
|
-
<v-text-field v-model="listData.name" label="List Name" required></v-text-field>
|
|
19
|
-
</v-col>
|
|
20
|
-
<v-col cols="6">
|
|
21
|
-
<v-select v-model="listData.type" label="Type"
|
|
22
|
-
:items="['List', 'Registry', 'Playlist', 'Todo']"></v-select>
|
|
23
|
-
</v-col>
|
|
24
|
-
<v-col cols="6">
|
|
25
|
-
<v-select v-model="listData.status" label="Status"
|
|
26
|
-
:items="['Public', 'Private']"></v-select>
|
|
27
|
-
</v-col>
|
|
28
|
-
<v-col cols="12">
|
|
29
|
-
<v-file-input @change="handleImageUpload" clearable
|
|
30
|
-
density="compact" prepend-icon="fas:fa fa-image" accept="image/*"
|
|
31
|
-
label="Image for List" variant="solo-inverted" />
|
|
32
|
-
</v-col>
|
|
33
|
-
<v-col cols="12">
|
|
34
|
-
<v-textarea v-model="listData.description" label="List Description"></v-textarea>
|
|
35
|
-
</v-col>
|
|
36
|
-
<v-col cols="12">
|
|
37
|
-
<v-card title="Choose a Product for your List">
|
|
38
|
-
<v-card-text>
|
|
39
|
-
<v-text-field density="compact" variant="solo"
|
|
40
|
-
label="Search Meeovi for products" append-inner-icon="fas:fa fa-search"
|
|
41
|
-
single-line hide-details></v-text-field>
|
|
42
|
-
<div class="d-flex pa-4">
|
|
43
|
-
<v-checkbox-btn v-model="includeFiles" class="pe-2" color="orange">
|
|
44
|
-
</v-checkbox-btn>
|
|
45
|
-
<!--<NuxtLink :to="`/product/${products.id}`">
|
|
46
|
-
<v-card class="ma-4" height="580" width="250" @click="toggle">
|
|
47
|
-
<NuxtImg loading="lazy" class="align-end text-white" height="280"
|
|
48
|
-
:src="`${products.featuredAsset.preview}`" :alt="products.name" cover />
|
|
49
|
-
|
|
50
|
-
<v-card-title class="pt-4">
|
|
51
|
-
{{ products.name }}
|
|
52
|
-
</v-card-title>
|
|
53
|
-
|
|
54
|
-
<v-card-text>
|
|
55
|
-
<div>Sku: {{ products.variants.sku }}</div>
|
|
56
|
-
</v-card-text>
|
|
57
|
-
|
|
58
|
-
<v-card-actions>
|
|
59
|
-
<v-card-title>$ {{ products.variants.price }}
|
|
60
|
-
</v-card-title>
|
|
61
|
-
</v-card-actions>
|
|
62
|
-
<div class="d-flex fill-height align-center justify-center">
|
|
63
|
-
<v-scale-transition>
|
|
64
|
-
<v-icon v-if="isSelected" color="white" size="48"
|
|
65
|
-
icon="mdi-close-circle-outline"></v-icon>
|
|
66
|
-
</v-scale-transition>
|
|
67
|
-
</div>
|
|
68
|
-
</v-card>
|
|
69
|
-
</NuxtLink>-->
|
|
70
|
-
</div>
|
|
71
|
-
</v-card-text>
|
|
72
|
-
</v-card>
|
|
73
|
-
</v-col>
|
|
74
|
-
</v-row>
|
|
75
|
-
</v-container>
|
|
76
|
-
<small>*indicates required field</small>
|
|
77
|
-
</v-card-text>
|
|
78
|
-
<v-divider class="mt-12"></v-divider>
|
|
79
|
-
<v-card-actions>
|
|
80
|
-
<v-btn color="blue-darken-1" variant="text" type="submit" @click="resetForm = false">
|
|
81
|
-
Reset
|
|
82
|
-
</v-btn>
|
|
83
|
-
<v-btn color="blue-darken-1" variant="text" type="submit">
|
|
84
|
-
Update
|
|
85
|
-
</v-btn>
|
|
86
|
-
</v-card-actions>
|
|
87
|
-
</form>
|
|
88
|
-
</v-card>
|
|
89
|
-
</v-row>
|
|
90
|
-
</div>
|
|
91
|
-
</template>
|
|
92
|
-
|
|
93
|
-
<script setup>
|
|
94
|
-
import { ref, onMounted } from 'vue';
|
|
95
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
96
|
-
import uploadFiles from '~//composables/uploadFiles';
|
|
97
|
-
import updateList from '~/app/composables/lists/updateList';
|
|
98
|
-
|
|
99
|
-
const route = useRoute();
|
|
100
|
-
const router = useRouter();
|
|
101
|
-
|
|
102
|
-
const listData = ref({
|
|
103
|
-
id: '', // Add this to store the list ID
|
|
104
|
-
name: '',
|
|
105
|
-
type: '',
|
|
106
|
-
status: '',
|
|
107
|
-
description: '',
|
|
108
|
-
image: null,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const dialog = ref(false);
|
|
112
|
-
const includeFiles = ref(true);
|
|
113
|
-
const imageFile = ref(null);
|
|
114
|
-
const loading = ref(false);
|
|
115
|
-
|
|
116
|
-
// Function to fetch existing list data
|
|
117
|
-
const fetchListData = async () => {
|
|
118
|
-
try {
|
|
119
|
-
const { $directus, $readItem } = useNuxtApp();
|
|
120
|
-
const listId = route.params.id; // Assuming you're passing the ID in the route
|
|
121
|
-
const response = await $directus.request(readItem('lists', listId));
|
|
122
|
-
|
|
123
|
-
// Populate the form with existing data
|
|
124
|
-
listData.value = {
|
|
125
|
-
id: response.id,
|
|
126
|
-
name: response.name,
|
|
127
|
-
type: response.type,
|
|
128
|
-
status: response.status,
|
|
129
|
-
description: response.description,
|
|
130
|
-
image: response.image
|
|
131
|
-
};
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.error('Error fetching list:', error);
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// Load existing data when component mounts
|
|
138
|
-
onMounted(() => {
|
|
139
|
-
if (route.params.id) {
|
|
140
|
-
fetchListData();
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const handleImageUpload = (event) => {
|
|
145
|
-
imageFile.value = event.target.files[0];
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const resetForm = () => {
|
|
149
|
-
listData.value = {
|
|
150
|
-
name: '',
|
|
151
|
-
type: '',
|
|
152
|
-
status: '',
|
|
153
|
-
description: '',
|
|
154
|
-
image: null,
|
|
155
|
-
};
|
|
156
|
-
imageFile.value = null;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const handleSubmit = async () => {
|
|
160
|
-
try {
|
|
161
|
-
loading.value = true;
|
|
162
|
-
|
|
163
|
-
// Handle image upload if there's a new image
|
|
164
|
-
if (imageFile.value) {
|
|
165
|
-
const uploadedFiles = await uploadFiles({
|
|
166
|
-
imageFile: imageFile.value,
|
|
167
|
-
});
|
|
168
|
-
listData.value.image = uploadedFiles.imageId;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Update the list
|
|
172
|
-
const updatedList = await updateList(route.params.id, {
|
|
173
|
-
name: listData.value.name,
|
|
174
|
-
type: listData.value.type,
|
|
175
|
-
status: listData.value.status,
|
|
176
|
-
description: listData.value.description,
|
|
177
|
-
image: listData.value.image,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
console.log('List updated successfully:', updatedList);
|
|
181
|
-
|
|
182
|
-
// Show success message (you can implement your preferred notification system)
|
|
183
|
-
alert('List updated successfully');
|
|
184
|
-
|
|
185
|
-
} catch (error) {
|
|
186
|
-
console.error('Error updating list:', error);
|
|
187
|
-
alert('Error updating list: ' + error.message);
|
|
188
|
-
} finally {
|
|
189
|
-
loading.value = false;
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
</script>
|
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-card class="media-player">
|
|
3
|
-
<div v-if="currentMedia">
|
|
4
|
-
<!-- Video Player -->
|
|
5
|
-
<video
|
|
6
|
-
v-if="currentMedia.media_type === 'video'"
|
|
7
|
-
ref="videoPlayer"
|
|
8
|
-
:src="currentMedia.url"
|
|
9
|
-
:poster="currentMedia.thumbnail"
|
|
10
|
-
controls
|
|
11
|
-
@loadedmetadata="onMediaLoaded"
|
|
12
|
-
@timeupdate="onTimeUpdate"
|
|
13
|
-
@ended="onMediaEnded"
|
|
14
|
-
class="w-100"
|
|
15
|
-
/>
|
|
16
|
-
|
|
17
|
-
<!-- Audio Player -->
|
|
18
|
-
<div v-else-if="currentMedia.media_type === 'audio'" class="audio-player">
|
|
19
|
-
<div class="d-flex align-center pa-4">
|
|
20
|
-
<v-img
|
|
21
|
-
:src="currentMedia.thumbnail || '/default-audio.png'"
|
|
22
|
-
width="80"
|
|
23
|
-
height="80"
|
|
24
|
-
class="rounded me-4"
|
|
25
|
-
/>
|
|
26
|
-
<div class="grow">
|
|
27
|
-
<h3 class="text-h6">{{ currentMedia.title }}</h3>
|
|
28
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
29
|
-
{{ currentMedia.metadata?.artist || 'Unknown Artist' }}
|
|
30
|
-
</p>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
<audio
|
|
35
|
-
ref="audioPlayer"
|
|
36
|
-
:src="currentMedia.url"
|
|
37
|
-
@loadedmetadata="onMediaLoaded"
|
|
38
|
-
@timeupdate="onTimeUpdate"
|
|
39
|
-
@ended="onMediaEnded"
|
|
40
|
-
style="display: none;"
|
|
41
|
-
/>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<!-- Controls -->
|
|
46
|
-
<v-card-actions class="px-4 py-2">
|
|
47
|
-
<v-btn
|
|
48
|
-
icon
|
|
49
|
-
@click="previousTrack"
|
|
50
|
-
:disabled="currentIndex === 0"
|
|
51
|
-
>
|
|
52
|
-
<v-icon>mdi-skip-previous</v-icon>
|
|
53
|
-
</v-btn>
|
|
54
|
-
|
|
55
|
-
<v-btn
|
|
56
|
-
icon
|
|
57
|
-
@click="togglePlayPause"
|
|
58
|
-
color="primary"
|
|
59
|
-
size="large"
|
|
60
|
-
>
|
|
61
|
-
<v-icon>{{ isPlaying ? 'mdi-pause' : 'mdi-play' }}</v-icon>
|
|
62
|
-
</v-btn>
|
|
63
|
-
|
|
64
|
-
<v-btn
|
|
65
|
-
icon
|
|
66
|
-
@click="nextTrack"
|
|
67
|
-
:disabled="currentIndex === playlist.length - 1"
|
|
68
|
-
>
|
|
69
|
-
<v-icon>mdi-skip-next</v-icon>
|
|
70
|
-
</v-btn>
|
|
71
|
-
|
|
72
|
-
<v-spacer />
|
|
73
|
-
|
|
74
|
-
<span class="text-caption">{{ formatTime(currentTime) }}</span>
|
|
75
|
-
<v-slider
|
|
76
|
-
v-model="currentTime"
|
|
77
|
-
:max="duration"
|
|
78
|
-
@update:model-value="seekTo"
|
|
79
|
-
class="mx-4"
|
|
80
|
-
style="min-width: 200px;"
|
|
81
|
-
hide-details
|
|
82
|
-
/>
|
|
83
|
-
<span class="text-caption">{{ formatTime(duration) }}</span>
|
|
84
|
-
|
|
85
|
-
<v-spacer />
|
|
86
|
-
|
|
87
|
-
<v-btn
|
|
88
|
-
icon
|
|
89
|
-
@click="toggleShuffle"
|
|
90
|
-
:color="shuffle ? 'primary' : 'default'"
|
|
91
|
-
>
|
|
92
|
-
<v-icon>mdi-shuffle</v-icon>
|
|
93
|
-
</v-btn>
|
|
94
|
-
|
|
95
|
-
<v-btn
|
|
96
|
-
icon
|
|
97
|
-
@click="toggleRepeat"
|
|
98
|
-
:color="repeat !== 'none' ? 'primary' : 'default'"
|
|
99
|
-
>
|
|
100
|
-
<v-icon>{{ repeatIcon }}</v-icon>
|
|
101
|
-
</v-btn>
|
|
102
|
-
|
|
103
|
-
<v-btn icon @click="showPlaylist = !showPlaylist">
|
|
104
|
-
<v-icon>mdi-playlist-music</v-icon>
|
|
105
|
-
</v-btn>
|
|
106
|
-
</v-card-actions>
|
|
107
|
-
|
|
108
|
-
<!-- Playlist -->
|
|
109
|
-
<v-expand-transition>
|
|
110
|
-
<div v-show="showPlaylist">
|
|
111
|
-
<v-divider />
|
|
112
|
-
<v-list class="playlist-list" max-height="300" style="overflow-y: auto;">
|
|
113
|
-
<v-list-item
|
|
114
|
-
v-for="(item, index) in playlist"
|
|
115
|
-
:key="item.id"
|
|
116
|
-
@click="playTrack(index)"
|
|
117
|
-
:active="index === currentIndex"
|
|
118
|
-
>
|
|
119
|
-
<template v-slot:prepend>
|
|
120
|
-
<v-img
|
|
121
|
-
:src="item.thumbnail || '/default-audio.png'"
|
|
122
|
-
width="40"
|
|
123
|
-
height="40"
|
|
124
|
-
class="rounded me-3"
|
|
125
|
-
/>
|
|
126
|
-
</template>
|
|
127
|
-
|
|
128
|
-
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
|
129
|
-
<v-list-item-subtitle>
|
|
130
|
-
{{ item.metadata?.artist || 'Unknown Artist' }}
|
|
131
|
-
</v-list-item-subtitle>
|
|
132
|
-
|
|
133
|
-
<template v-slot:append>
|
|
134
|
-
<span class="text-caption">{{ formatTime(item.duration) }}</span>
|
|
135
|
-
</template>
|
|
136
|
-
</v-list-item>
|
|
137
|
-
</v-list>
|
|
138
|
-
</div>
|
|
139
|
-
</v-expand-transition>
|
|
140
|
-
</v-card>
|
|
141
|
-
</template>
|
|
142
|
-
|
|
143
|
-
<script setup>
|
|
144
|
-
const props = defineProps({
|
|
145
|
-
playlist: {
|
|
146
|
-
type: Array,
|
|
147
|
-
required: true
|
|
148
|
-
},
|
|
149
|
-
autoplay: {
|
|
150
|
-
type: Boolean,
|
|
151
|
-
default: false
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
const currentIndex = ref(0)
|
|
156
|
-
const isPlaying = ref(false)
|
|
157
|
-
const currentTime = ref(0)
|
|
158
|
-
const duration = ref(0)
|
|
159
|
-
const shuffle = ref(false)
|
|
160
|
-
const repeat = ref('none') // 'none', 'one', 'all'
|
|
161
|
-
const showPlaylist = ref(false)
|
|
162
|
-
|
|
163
|
-
const videoPlayer = ref(null)
|
|
164
|
-
const audioPlayer = ref(null)
|
|
165
|
-
|
|
166
|
-
const currentMedia = computed(() => {
|
|
167
|
-
return props.playlist[currentIndex.value] || null
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
const currentPlayer = computed(() => {
|
|
171
|
-
return currentMedia.value?.media_type === 'video' ? videoPlayer.value : audioPlayer.value
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
const repeatIcon = computed(() => {
|
|
175
|
-
switch (repeat.value) {
|
|
176
|
-
case 'one': return 'mdi-repeat-once'
|
|
177
|
-
case 'all': return 'mdi-repeat'
|
|
178
|
-
default: return 'mdi-repeat-off'
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
const togglePlayPause = () => {
|
|
183
|
-
if (!currentPlayer.value) return
|
|
184
|
-
|
|
185
|
-
if (isPlaying.value) {
|
|
186
|
-
currentPlayer.value.pause()
|
|
187
|
-
} else {
|
|
188
|
-
currentPlayer.value.play()
|
|
189
|
-
}
|
|
190
|
-
isPlaying.value = !isPlaying.value
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const playTrack = (index) => {
|
|
194
|
-
currentIndex.value = index
|
|
195
|
-
nextTick(() => {
|
|
196
|
-
if (currentPlayer.value) {
|
|
197
|
-
currentPlayer.value.play()
|
|
198
|
-
isPlaying.value = true
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const nextTrack = () => {
|
|
204
|
-
if (shuffle.value) {
|
|
205
|
-
currentIndex.value = Math.floor(Math.random() * props.playlist.length)
|
|
206
|
-
} else if (currentIndex.value < props.playlist.length - 1) {
|
|
207
|
-
currentIndex.value++
|
|
208
|
-
} else if (repeat.value === 'all') {
|
|
209
|
-
currentIndex.value = 0
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
nextTick(() => {
|
|
213
|
-
if (currentPlayer.value && isPlaying.value) {
|
|
214
|
-
currentPlayer.value.play()
|
|
215
|
-
}
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const previousTrack = () => {
|
|
220
|
-
if (currentIndex.value > 0) {
|
|
221
|
-
currentIndex.value--
|
|
222
|
-
} else if (repeat.value === 'all') {
|
|
223
|
-
currentIndex.value = props.playlist.length - 1
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
nextTick(() => {
|
|
227
|
-
if (currentPlayer.value && isPlaying.value) {
|
|
228
|
-
currentPlayer.value.play()
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const seekTo = (time) => {
|
|
234
|
-
if (currentPlayer.value) {
|
|
235
|
-
currentPlayer.value.currentTime = time
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const toggleShuffle = () => {
|
|
240
|
-
shuffle.value = !shuffle.value
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const toggleRepeat = () => {
|
|
244
|
-
const modes = ['none', 'all', 'one']
|
|
245
|
-
const currentIndex = modes.indexOf(repeat.value)
|
|
246
|
-
repeat.value = modes[(currentIndex + 1) % modes.length]
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const onMediaLoaded = () => {
|
|
250
|
-
if (currentPlayer.value) {
|
|
251
|
-
duration.value = currentPlayer.value.duration || 0
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const onTimeUpdate = () => {
|
|
256
|
-
if (currentPlayer.value) {
|
|
257
|
-
currentTime.value = currentPlayer.value.currentTime || 0
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const onMediaEnded = () => {
|
|
262
|
-
isPlaying.value = false
|
|
263
|
-
|
|
264
|
-
if (repeat.value === 'one') {
|
|
265
|
-
currentPlayer.value.currentTime = 0
|
|
266
|
-
currentPlayer.value.play()
|
|
267
|
-
isPlaying.value = true
|
|
268
|
-
} else {
|
|
269
|
-
nextTrack()
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const formatTime = (seconds) => {
|
|
274
|
-
if (!seconds || isNaN(seconds)) return '0:00'
|
|
275
|
-
const mins = Math.floor(seconds / 60)
|
|
276
|
-
const secs = Math.floor(seconds % 60)
|
|
277
|
-
return `${mins}:${secs.toString().padStart(2, '0')}`
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
onMounted(() => {
|
|
281
|
-
if (props.autoplay && currentMedia.value) {
|
|
282
|
-
nextTick(() => {
|
|
283
|
-
togglePlayPause()
|
|
284
|
-
})
|
|
285
|
-
}
|
|
286
|
-
})
|
|
287
|
-
</script>
|
|
288
|
-
|
|
289
|
-
<style scoped>
|
|
290
|
-
.media-player {
|
|
291
|
-
max-width: 100%;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.audio-player {
|
|
295
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
296
|
-
color: white;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
.playlist-list {
|
|
300
|
-
background-color: rgba(0, 0, 0, 0.02);
|
|
301
|
-
}
|
|
302
|
-
</style>
|