feat: Implement core classroom functionality including video player, learning and quiz pages, course detail view, and i18n support.

This commit is contained in:
supalerk-ar66 2026-02-12 16:05:37 +07:00
parent 008f712480
commit 7f5119e5aa
9 changed files with 289 additions and 109 deletions

View file

@ -165,9 +165,21 @@ const togglePlay = () => {
return;
}
if (!videoRef.value) return;
if (isPlaying.value) videoRef.value.pause();
else videoRef.value.play();
isPlaying.value = !isPlaying.value;
if (isPlaying.value) {
videoRef.value.pause();
isPlaying.value = false;
} else {
const playPromise = videoRef.value.play();
if (playPromise !== undefined) {
playPromise.then(() => {
isPlaying.value = true;
}).catch(error => {
// Auto-play was prevented or play was interrupted
// We can safely ignore this error
console.log("Video play request handled:", error.name);
});
}
}
};
const handleTimeUpdate = () => {
@ -238,12 +250,12 @@ watch([volume, isMuted], () => {
></iframe>
<!-- 2. Standard HTML5 Video Player -->
<div v-else class="w-full h-full relative">
<div v-else class="w-full h-full relative group/video cursor-pointer">
<video
ref="videoRef"
:src="src"
:poster="poster"
class="w-full h-full object-contain"
class="w-full h-full object-contain bg-slate-900"
@click="togglePlay"
@timeupdate="handleTimeUpdate"
@loadedmetadata="handleLoadedMetadata"
@ -251,26 +263,30 @@ watch([volume, isMuted], () => {
/>
<!-- Custom Controls Overlay (Only for HTML5 Video) -->
<div class="absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/80 via-black/40 to-transparent transition-opacity opacity-0 group-hover:opacity-100">
<div class="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-black/90 via-black/40 to-transparent transition-opacity opacity-0 group-hover/video:opacity-100 flex flex-col gap-3">
<!-- Progress Bar -->
<div class="relative flex-grow h-1.5 bg-white/20 rounded-full cursor-pointer group/progress overflow-hidden" @click="seek">
<div class="absolute top-0 left-0 h-full bg-blue-500 rounded-full group-hover/progress:bg-blue-400 transition-all shadow-[0_0_12px_rgba(59,130,246,0.6)]" :style="{ width: videoProgress + '%' }"></div>
</div>
<div class="flex items-center gap-4 text-white">
<q-btn flat round dense :icon="isPlaying ? 'pause' : 'play_arrow'" @click.stop="togglePlay" />
<div class="relative flex-grow h-1.5 bg-white/20 rounded-full cursor-pointer group/progress overflow-hidden" @click="seek">
<div class="absolute top-0 left-0 h-full bg-blue-500 rounded-full group-hover/progress:bg-blue-400 transition-all shadow-[0_0_10px_rgba(59,130,246,0.5)]" :style="{ width: videoProgress + '%' }"></div>
</div>
<span class="text-xs font-mono font-medium opacity-90">{{ currentTimeDisplay }} / {{ durationDisplay }}</span>
<q-btn flat round dense :icon="isPlaying ? 'pause' : 'play_arrow'" @click.stop="togglePlay" class="hover:scale-110 active:scale-95 transition-transform" />
<span class="text-xs font-mono font-bold opacity-80">{{ currentTimeDisplay }} / {{ durationDisplay }}</span>
<div class="flex-grow"></div>
<!-- Volume Control -->
<div class="flex items-center gap-2 group/volume">
<q-btn flat round dense :icon="volumeIcon" @click.stop="handleToggleMute" color="white" />
<div class="w-0 group-hover/volume:w-20 overflow-hidden transition-all duration-300 flex items-center">
<div class="flex items-center gap-2 group/volume relative">
<q-btn flat round dense :icon="volumeIcon" @click.stop="handleToggleMute" color="white" class="hover:scale-110 transition-transform" />
<div class="w-0 group-hover/volume:w-24 overflow-hidden transition-all duration-300 flex items-center bg-black/60 backdrop-blur-md rounded-full px-2">
<input
type="range"
min="0"
max="1"
step="0.1"
step="0.05"
:value="volume"
@input="handleVolumeChange"
class="w-20 h-1 bg-white/30 rounded-lg appearance-none cursor-pointer accent-blue-500"
class="w-20 h-1 bg-white/20 rounded-lg appearance-none cursor-pointer accent-blue-500 mx-2"
/>
</div>
</div>