Files
NSG_PORTAL_V2/components/dynamic-page/page-component/templates/articles/details/Podcast.vue
T

480 lines
13 KiB
Vue
Raw Normal View History

2024-06-28 15:39:26 +07:00
<script setup lang="ts">
2024-07-09 15:15:22 +07:00
import { useArticleStore } from "~/stores/articles";
import Poll from "~/components/article/immerse/Poll.vue";
import Quiz from "~/components/article/immerse/Quiz.vue";
import Survey from "~/components/article/immerse/Survey.vue";
import Document from "~/components/article/immerse/Document.vue";
import Attachment from "@/components/article/immerse/Attachment.vue";
import Tag from "@/components/article/immerse/Tag.vue";
const { currentArticle } = storeToRefs(useArticleStore());
import { useDynamicPageStore } from "~/stores/dynamic-page";
import { useCategoryStore } from "~/stores/category";
2024-06-28 15:39:26 +07:00
2024-07-09 15:15:22 +07:00
const store = reactive({
dynamicPage: useDynamicPageStore(),
article: useArticleStore(),
category: useCategoryStore(),
});
const { categoryTree } = storeToRefs(store.category);
await store.category.fetchBySiteId();
const currentCategoryTree = (store.category.currentCategoryTree = findElementPathById(categoryTree.value, currentArticle.value.categoryId));
function findElementPathById(categories: any[], targetId: number, path: any[] = []) {
for (const category of categories) {
const currentPath = [...path, { title: category.title, code: category.code }];
if (category.id === targetId) {
return currentPath;
}
if (category.children) {
const result: any = findElementPathById(category.children, targetId, currentPath);
if (result) {
return result;
}
}
}
return null;
}
onMounted(async () => {
clickElement("figure", "custom-figure", "data-code");
clickElement("author", "author", "data-code");
let detailEmagazine = document.querySelector('div[layout="ARTICLE_DETAIL_EMAGAZINE"]');
let breadcrumb = document.querySelector('div[layout="BREADCRUM_DEFAULT"]');
if (detailEmagazine && breadcrumb) {
breadcrumb.classList.add("lg:max-w-640px", "mx-auto");
}
});
function clickElement(type: string, selector: string, attribute: string) {
const elements = document.querySelectorAll(selector);
elements.forEach((element) => {
element.addEventListener("click", (event) => {
event.preventDefault();
const url = `${window.location.protocol}//${window.location.host}/${type}/${element.getAttribute(attribute)}`;
const a = document.createElement("a");
a.href = url;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
});
}
const isBookmark = ref(false);
const onClickBookmark = () => {
isBookmark.value = !isBookmark.value;
};
async function copyLink() {
try {
const url = window.location.href;
await navigator.clipboard.writeText(url);
alert("copy link thành công");
} catch (error) {
alert(error);
}
}
const getSrc = (htmlString: string) => {
const srcRegex = /src="([^"]+)"/;
return htmlString?.match(srcRegex);
};
const isMoreControl = ref(false);
const isPlayed = ref(true);
const isVolume = ref(true);
const speedList = ref<{ [key: number]: string }>({
1: "0.5x",
2: "0.75x",
3: "1.0x",
4: "1.25x",
5: "1.50x",
});
const speedIndexDefault = ref(3);
const speedDefault = ref(speedList.value[speedIndexDefault.value]);
const volume = ref(1.0);
const audioPlayer = ref<HTMLAudioElement | null>(null);
const currentTime = ref(0);
const duration = ref(0);
function setUpVolums() {
isVolume.value = !isVolume.value;
if (audioPlayer.value) {
if (isVolume.value) {
audioPlayer.value.volume = 1;
} else {
audioPlayer.value.volume = 0;
}
}
}
const updateVolume = (num?: number) => {
if (audioPlayer.value) {
if(num) {
volume.value += num
}
// console.log('1231321')
audioPlayer.value.volume = volume.value;
}
};
function chanageSpeed() {
if (speedIndexDefault.value < 5) {
speedIndexDefault.value += 1;
if (audioPlayer.value) {
audioPlayer.value.playbackRate += 0.25;
}
speedDefault.value = speedList.value[speedIndexDefault.value];
} else {
if (audioPlayer.value) {
audioPlayer.value.playbackRate = 0.5;
}
speedIndexDefault.value = 1;
speedDefault.value = speedList.value[1];
}
}
function togglePlayer() {
isPlayed.value = !isPlayed.value;
if (audioPlayer.value) {
if (isPlayed.value) {
audioPlayer.value.pause();
} else {
audioPlayer.value.play();
}
}
}
function replayAndForward(time: number) {
if (audioPlayer.value) {
if (audioPlayer.value.currentTime == audioPlayer.value.duration) {
isPlayed.value = true;
} else {
audioPlayer.value.currentTime = audioPlayer.value.currentTime + time;
}
}
}
const seekToTime = () => {
if (audioPlayer.value) {
audioPlayer.value.currentTime = currentTime.value;
}
};
const updateCurrentTime = () => {
if (audioPlayer.value) {
currentTime.value = audioPlayer.value.currentTime;
}
};
const updateDuration = () => {
if (audioPlayer.value) {
duration.value = audioPlayer.value.duration;
}
};
const currrentTimeComputed = computed(() => {
return utils.formattedTime(currentTime.value);
});
const durationComputed = computed(() => {
return utils.formattedTime(duration.value);
});
2024-06-28 15:39:26 +07:00
</script>
<template>
2024-07-09 15:15:22 +07:00
<div class="lg:p-40px md:p-30px p-5 border-1px border-solid border-black/10 rounded-8px">
<div class="flex md:flex-row flex-col md:gap-6 gap-2 justify-between mb-10px">
<p class="text-#9f9f9f text-14px mb-2 md:hidden block text-center">
{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}
2024-06-28 15:39:26 +07:00
</p>
2024-07-09 15:15:22 +07:00
<figure class="!w-auto"><img class="w-150px h-150px rounded-8px shadow-md cursor-pointer" :src="currentArticle?.thumbnail" alt="Ảnh podcast" title="Ảnh podcast" /></figure>
<div class="flex-1 text-#222 m-0 md:text-left text-center">
<p class="text-#9f9f9f text-14px mb-2 md:block hidden">
{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}
2024-06-28 15:39:26 +07:00
</p>
2024-07-09 15:15:22 +07:00
<h1 class="text-24px md:mb-4 mb-2 font-bold" v-html="currentArticle?.title"></h1>
<p class="hidden md:line-clamp-3" v-html="currentArticle?.intro"></p>
2024-06-28 15:39:26 +07:00
</div>
2024-07-09 15:15:22 +07:00
<ul class="items-start gap-2 m-0 p-0 md:flex hidden">
<li class="w-9 h-9 bg-white border-1 border-solid border-[rgb(229, 231, 235)] cursor-pointer shadow-md rounded-50px relative hover:bg-primary-100 hover:text-primary-600">
<Icon class="text-18px absolute top-50% left-50% translate-x--50% translate-y--50%" name="mdi:bookmark-outline" />
</li>
<li class="w-9 h-9 bg-white border-1 border-solid border-[rgb(229, 231, 235)] cursor-pointer shadow-md rounded-50px relative hover:bg-primary-100 hover:text-primary-600">
<Icon class="text-18px absolute top-50% left-50% translate-x--50% translate-y--50%" name="material-symbols:mode-comment-outline" />
</li>
2024-06-28 15:39:26 +07:00
</ul>
</div>
2024-07-09 15:15:22 +07:00
<audio :src="getSrc(currentArticle?.detail)?.[1]" preload="auto" ref="audioPlayer" @timeupdate="updateCurrentTime" @loadedmetadata="updateDuration" />
<div class="p-2">
<input class="w-full accent-primary-600 cursor-pointer" type="range" v-model="currentTime" @input="seekToTime" :max="duration" />
<div class="flex justify-between">
<span>{{ currrentTimeComputed }}</span>
<span>{{ durationComputed }}</span>
2024-06-28 15:39:26 +07:00
</div>
2024-07-09 15:15:22 +07:00
<div class="flex justify-between items-center">
<div class="md:w-150px text-left">
<div class="text-28px text-primary-600 md:hidden block">
2024-06-28 15:39:26 +07:00
<Icon name="material-symbols:skip-previous" />
</div>
2024-07-09 15:15:22 +07:00
<div class="md:inline-flex hidden items-center gap-2 ml--10px h9 text-primary-600 rounded-8px text-28px cursor-pointer hover:bg-primary-100">
<Icon @click="updateVolume(-0.1)" name="material-symbols:volume-mute"></Icon>
<input v-if="isVolume" class="accent-primary-600 h-1 w-12 lg:w-20 cursor-pointer" type="range" v-model="volume" @input="updateVolume()" min="0.1" max="1" step="0.1" />
<Icon @click="updateVolume(0.1)" name="material-symbols:volume-up"></Icon>
2024-06-28 15:39:26 +07:00
</div>
</div>
2024-07-09 15:15:22 +07:00
<div class="flex items-center justify-center gap-4 flex-1 text-28px text-primary-600">
<Icon @click="replayAndForward(-10)" name="fluent:skip-back-10-48-filled" />
<button @click="togglePlayer" class="bg-transparent">
<Icon v-if="isPlayed" name="material-symbols:play-arrow-rounded" class="text-64px" />
<Icon v-if="!isPlayed" name="material-symbols:pause" class="text-64px" />
</button>
<Icon @click="replayAndForward(10)" name="fluent:skip-forward-10-48-filled" />
2024-06-28 15:39:26 +07:00
</div>
2024-07-09 15:15:22 +07:00
<div class="md:w-150px text-right">
<div class="text-28px text-primary-600 md:hidden block">
2024-06-28 15:39:26 +07:00
<Icon name="material-symbols:skip-next" />
</div>
2024-07-09 15:15:22 +07:00
<div class="text-14px text-primary-600 md:block hidden cursor-pointer" @click="chanageSpeed">
<span class="font-300">Tốc độ phát: </span>
<strong class="font-bold text-20px ml-1">{{ speedDefault }}</strong>
2024-06-28 15:39:26 +07:00
</div>
</div>
</div>
</div>
2024-07-09 15:15:22 +07:00
<p class="md:hidden block" v-html="currentArticle?.intro"></p>
2024-06-28 15:39:26 +07:00
</div>
</template>
<style lang="scss">
:root {
--podcast-wrapper-padding: 40;
}
.podcast-padding-tablet {
--podcast-wrapper-padding: 30;
}
.podcast-padding-smartphone {
--podcast-wrapper-padding: 20;
}
.podcast__wrapper {
padding: calc(var(--podcast-wrapper-padding) * 1px);
border: 1px solid #eeeeee;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.1);
border-radius: 8px;
.podcast {
display: flex;
justify-content: space-between;
gap: 24px;
align-items: center;
margin-bottom: 10px;
& > figure > img {
width: 150px;
height: 150px;
border-radius: 8px;
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
cursor: pointer;
}
&__content__text {
font-size: 18px;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
&__content {
flex: 1;
color: #222222;
margin: 0;
&__time {
color: #9f9f9f;
font-size: 14px;
margin-bottom: 8px;
}
&__title {
font-size: 24px;
margin-bottom: 16px;
font-weight: bold;
}
@media (max-width: 768px) {
text-align: center;
&__time {
display: none;
}
&__text {
display: none;
}
}
}
& > .buttons {
display: flex;
align-self: start;
gap: 8px;
margin: 0;
padding: 0;
& li {
list-style: none;
width: 36px;
height: 36px;
background-color: white;
border: 1px solid rgb(229, 231, 235);
cursor: pointer;
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
border-radius: 50px;
position: relative;
&:hover {
background-color: #e6f4ff;
color: #3c7abc;
}
& svg {
font-size: 18px;
position: absolute;
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-55%);
}
}
}
@media (max-width: 768px) {
flex-direction: column;
& .buttons {
display: none;
}
}
}
.playlist {
padding: 8px;
&__time {
display: flex;
justify-content: space-between;
padding-top: 12px;
position: relative;
&::after,
&::before {
content: "";
position: absolute;
height: 4px;
top: 0;
cursor: pointer;
}
&::after {
width: 100%;
background-color: #e6f4ff;
z-index: 1;
}
&::before {
width: 50%;
background-color: #3c7abc;
z-index: 2;
}
& span {
font-size: 16px;
color: #3c7abc;
font-weight: 500;
}
}
&__buttons {
display: flex;
justify-content: space-between;
align-items: center;
& .button__prev,
& .button__next {
font-size: 28px;
color: #3c7abc;
}
& .sound {
display: inline-flex;
align-items: center;
gap: 8px;
margin-left: -10px;
padding: 0 10px;
height: 36px;
color: #3c7abc;
border-radius: 8px;
font-size: 28px;
cursor: pointer;
&:hover {
background-color: #e6f4ff;
}
& > div {
width: 50px;
height: 2px;
position: relative;
background-color: #dcf0ff;
&::after {
position: absolute;
content: "";
top: 0;
height: 2px;
width: 50%;
background-color: #3c7abc;
}
}
}
& .play {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
flex: 1;
& svg {
font-size: 28px;
color: #3c7abc;
&.button {
font-size: 64px;
}
}
}
& .speed {
font-size: 14px;
color: #3c7abc;
& span {
font-weight: 200;
}
& strong {
font-weight: bold;
font-size: 20px;
margin-left: 4px;
}
}
}
}
}
</style>