phongdt:podcast
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import AudioPlayer from "~/organisms/audioPlayer/AudioPlayer.vue";
|
||||||
|
import ArticlePodcast from "~/organisms/article/ArticlePodcast.vue";
|
||||||
|
|
||||||
|
const props = defineProps<{ article: any }>();
|
||||||
|
|
||||||
|
const getSrc = (htmlString: string) => {
|
||||||
|
const srcRegex = /src="([^"]+)"/;
|
||||||
|
return htmlString?.match(srcRegex);
|
||||||
|
};
|
||||||
|
const getArticleById = async (articleId: number) => {
|
||||||
|
try {
|
||||||
|
const { apiUrl } = useRuntimeConfig().public;
|
||||||
|
const { item }: any = await $fetch(`${apiUrl}/cms/article/${articleId}`, {
|
||||||
|
headers: {
|
||||||
|
Site: "1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return item;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const listArticle = computed(() => {
|
||||||
|
// if (props.article && props.article.audioIds) {
|
||||||
|
// return props.article.audioIds.split(",").map((audioId: number) => getArticleById(audioId));
|
||||||
|
// }
|
||||||
|
// return []
|
||||||
|
const test = "8,2,3";
|
||||||
|
return test.split(",").map((audioId) => getArticleById(audioId));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full grid grid-cols-12 gap-4" v-if="listArticle?.length > 0">
|
||||||
|
<div class="col-span-9 h-60 md:h-100 relative bg-center" :style="'background-image: url(' + listArticle[0]?.thumbnail + '); background-size: cover;'">
|
||||||
|
<div class="absolute inset-0 bg-black opacity-80 z-1"></div>
|
||||||
|
<Wrap class="relative flex items-center justify-center">
|
||||||
|
<div class="w-full h-40 md:h-80 absolute inset-0 z-2">
|
||||||
|
<div class="grid grid-cols-10 w-full">
|
||||||
|
<div class="col-span-3 flex justify-center items-center h-60 md:h-80 mx-2 md:mx-8">
|
||||||
|
<div
|
||||||
|
class="h-40 md:h-60 w-full rounded-tl-3xl rounded-br-3xl border-double px-2 overflow-x-hidden relative overflow-y-hidden bg-cover z-1 after:z-2 after:content-[''] after:w-full after:h-full after:top-0 after:left-0 after:bg-#000 after:opacity-30 after:absolute"
|
||||||
|
:style="{ backgroundImage: `url(${listArticle[0]?.thumbnail})` }"
|
||||||
|
>
|
||||||
|
<img :src="listArticle[0]?.thumbnail" alt="" class="relative z-3 h-40 md:h-60 w-full object-contain" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-7 grid grid-cols-12 relative">
|
||||||
|
<div class="col-span-12 w-full grid grid-cols-12 mt-8 md:mb-4">
|
||||||
|
<div class="col-span-11">
|
||||||
|
<h1 v-html="listArticle[0]?.sub" class="text-xl font-bold text-white opacity-60"></h1>
|
||||||
|
<h1 class="text-md md:text-3xl text-[#fff] font-bold font-['SFD']">{{ listArticle[0]?.title }}</h1>
|
||||||
|
<time class="xs:mt-0.5 text-[10px] md:text-sm text-[#fff]">
|
||||||
|
{{ utils.dateFormat(listArticle[0]?.createdOn, "dddd, DD/MM/YYYY - HH:mm") }}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-12 w-full mb-4 hidden md:block">
|
||||||
|
<div v-html="listArticle[0]?.intro" class="text-left text-xl text-[#fff] font-['SFD']"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-11">
|
||||||
|
<AudioPlayer :src="getSrc(listArticle[0]?.detail)?.[1]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Wrap>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-3">
|
||||||
|
<div class="flex items-center mt-5 mb-5">
|
||||||
|
<ul class="bg-red-500 text-white text-sm font-semibold hover:bg-red-400 font-medium inline-block rounded-tl-lg rounded-br-lg">
|
||||||
|
<li class="inline-block uppercase rounded-l-lg border-radius border-red-500 border-r-0 px-2 py-1 text-center block transition-transform duration-300 transform hover:scale-105">Podcast Hôm nay</li>
|
||||||
|
</ul>
|
||||||
|
<div class="border border-slate-7 flex-grow ml-4"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid w-full">
|
||||||
|
<div class="" v-for="(item, index) in listArticle.slice(1)" :key="index">
|
||||||
|
<ArticlePodcast mode="podcast" image-size="md" text-size="lg" :createdOn="item?.createdOn" :title="item?.title" :slug="'/' + item.category?.code + '/' + item.code" :thumb="item?.thumbnail" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.name {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
|
||||||
|
<script setup lang='ts'>
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
thumb: string
|
||||||
|
title: string
|
||||||
|
brief?: string
|
||||||
|
slug: string
|
||||||
|
createdOn : string
|
||||||
|
titleClass?: string
|
||||||
|
clampIntro?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const defaultClass = {
|
||||||
|
article: [
|
||||||
|
'group',
|
||||||
|
'max-w-full',
|
||||||
|
'grid', 'gap-3',
|
||||||
|
'overflow-hidden',
|
||||||
|
'p-4'
|
||||||
|
],
|
||||||
|
thumbnail: [
|
||||||
|
'rounded-3xl', 'shadow-md', 'max-w-full', 'w-full', 'aspect-5/3',
|
||||||
|
'group-hover:scale-[1.05]',
|
||||||
|
'duration-500', 'ease-in-out','object-cover'
|
||||||
|
],
|
||||||
|
title: [
|
||||||
|
'font-bold', 'px-4', 'md:px-0',
|
||||||
|
props.titleClass ? props.titleClass : [
|
||||||
|
'xl:text-xl', 'text-base'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
brief: [
|
||||||
|
'text-sm', 'sm:text-base',
|
||||||
|
'mx-4', 'pb-4', 'md:pb-0', 'md:mx-0',
|
||||||
|
'border-b', 'border-stone-400', 'md:border-none',props.clampIntro
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NuxtLink :to="slug || '#!'" :title="title">
|
||||||
|
<article mode="basic" :class="defaultClass.article">
|
||||||
|
<div class="rounded-sm overflow-hidden relative">
|
||||||
|
<NuxtImg :src="thumb || '/images/default-thumbnail.jpg'" placeholder fit="cover" :class="defaultClass.thumbnail" loading="lazy" />
|
||||||
|
<div class="absolute bottom-2 left-0 bg-stone-200/[.56] h-10 flex justify-center items-center w-20 rounded-full">
|
||||||
|
<span class="icon">
|
||||||
|
<svg width="30" height="30" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.0131 0.5C5.38869 0.5 0 5.88331 0 12.5C0 19.1167 5.38869 24.5 12.0131 24.5C18.6376 24.5 24.0263 19.1167 24.0263 12.5C24.0263 5.88331 18.6376 0.5 12.0131 0.5ZM16.7889 12.9204L9.78122 17.4204C9.6991 17.4736 9.60426 17.5 9.51041 17.5C9.42829 17.5 9.34518 17.4795 9.2709 17.439C9.10957 17.3511 9.00985 17.1831 9.00985 17V8C9.00985 7.81691 9.10957 7.64891 9.2709 7.56102C9.42927 7.47411 9.62773 7.47945 9.78122 7.57958L16.7889 12.0796C16.9316 12.1714 17.0186 12.3301 17.0186 12.5C17.0186 12.6699 16.9316 12.8286 16.7889 12.9204Z" fill="#FF0000"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<!-- Nghe tập này -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="xs:mt-0.5 xs:text-sm text-sm">{{ utils.dateFormat(createdOn) }}</p>
|
||||||
|
<h3 :class="defaultClass.title" v-html="title"></h3>
|
||||||
|
<div v-if='brief' :class="defaultClass.brief" v-html="brief"> </div>
|
||||||
|
</article>
|
||||||
|
</NuxtLink>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
src?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
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 = () => {
|
||||||
|
if (audioPlayer.value) {
|
||||||
|
audioPlayer.value.volume = volume.value;
|
||||||
|
if (volume.value == 0) {
|
||||||
|
isVolume.value = false
|
||||||
|
} else {
|
||||||
|
isVolume.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<audio :src="src" preload="auto" ref="audioPlayer" @timeupdate="updateCurrentTime" @loadedmetadata="updateDuration" />
|
||||||
|
<div class="relative">
|
||||||
|
<div class="w-full">
|
||||||
|
<input class="w-full accent-[#fff] cursor-pointer" type="range" v-model="currentTime" @input="seekToTime" :max="duration" />
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between px-4 ">
|
||||||
|
<span class="text-[10px] sm:text-lg font-normal font-[Raleway] text-[#fff]">{{ currrentTimeComputed }}</span>
|
||||||
|
<span class="text-[10px] sm:text-lg font-normal font-[Raleway] text-[#fff]">{{ durationComputed }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full px-4 ">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<button @click="chanageSpeed"
|
||||||
|
class="text-[#fff] bg-transparent text-sm sm:text-sm lg:text-lg flex gap-1"><span
|
||||||
|
class="hidden lg:block">Tốc độ phát </span> <span
|
||||||
|
class="font-bold text-[12px] sm:text-sm lg:text-lg">{{
|
||||||
|
speedDefault
|
||||||
|
}}</span> </button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button @click="replayAndForward(-5)" class="hover:bg-stone-200 bg-transparent p-2 rounded-full">
|
||||||
|
<Icon name="ri:replay-5-fill" color="fff" class="text-lg sm:text-2xl md:text-4xl" style="" />
|
||||||
|
</button>
|
||||||
|
<button @click="togglePlayer" class="bg-transparent">
|
||||||
|
<Icon v-if="isPlayed" name="ri:play-circle-fill" color="fff"
|
||||||
|
class="text-lg sm:text-3xl md:text-6xl" />
|
||||||
|
<Icon v-if="!isPlayed" name="ri:pause-circle-fill" color="fff"
|
||||||
|
class="text-lg sm:text-3xl md:text-6xl" />
|
||||||
|
</button>
|
||||||
|
<button @click="replayAndForward(5)" class="hover:bg-stone-200 bg-transparent p-2 rounded-full">
|
||||||
|
<Icon name="ri:forward-5-line" color="fff" class="text-lg sm:text-2xl md:text-4xl" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<button class="bg-transparent">
|
||||||
|
<div class="flex gap-2 ">
|
||||||
|
<Icon v-if="isVolume" @click="setUpVolums()" name="ri:volume-up-fill" color="fff"
|
||||||
|
class="text-lg md:text-2xl lg:text-3xl" />
|
||||||
|
<Icon v-if="!isVolume" @click="setUpVolums()" name="ri:volume-mute-fill" color="fff"
|
||||||
|
class="text-lg md:text-2xl lg:text-3xl" />
|
||||||
|
<input v-if="isVolume" class="accent-[#fff] w-12 lg:w-20 cursor-pointer" type="range" v-model="volume"
|
||||||
|
@input="updateVolume" min="0" max="1" step="0.1" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div></template>
|
||||||
+2
-1
@@ -3,7 +3,7 @@ import _cloneDeep from 'lodash/cloneDeep';
|
|||||||
|
|
||||||
import DynamicTemplate from "~/components/dynamic-page/page/templates/index.vue";
|
import DynamicTemplate from "~/components/dynamic-page/page/templates/index.vue";
|
||||||
import DynamicSection from "~/components/dynamic-page/page-section/templates/index.vue";
|
import DynamicSection from "~/components/dynamic-page/page-section/templates/index.vue";
|
||||||
|
import Podcast from "~/components/article/podcast.vue"
|
||||||
import { useDynamicPageStore } from '~/stores/dynamic-page';
|
import { useDynamicPageStore } from '~/stores/dynamic-page';
|
||||||
const { currentPage, sectionPublished, componentPublished } = storeToRefs(useDynamicPageStore());
|
const { currentPage, sectionPublished, componentPublished } = storeToRefs(useDynamicPageStore());
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ useHead({
|
|||||||
<template>
|
<template>
|
||||||
<main class="h-screen" v-if="currentPage">
|
<main class="h-screen" v-if="currentPage">
|
||||||
<DynamicTemplate :settings="currentPage.settings">
|
<DynamicTemplate :settings="currentPage.settings">
|
||||||
|
<Podcast></Podcast>
|
||||||
<template v-if="sectionPublished && sectionPublished.length > 0">
|
<template v-if="sectionPublished && sectionPublished.length > 0">
|
||||||
<DynamicSection
|
<DynamicSection
|
||||||
v-for="(section, index) in sectionPublished"
|
v-for="(section, index) in sectionPublished"
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { utils } from "~/utils/utilities";
|
||||||
|
import { Author } from "./author";
|
||||||
|
import Base from "./base";
|
||||||
|
import { H3Event } from "h3";
|
||||||
|
import { ref } from "vue"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an article.
|
||||||
|
*/
|
||||||
|
export type Article = {
|
||||||
|
id: number;
|
||||||
|
siteId: number;
|
||||||
|
articleId: number;
|
||||||
|
categoryId: number;
|
||||||
|
sub?: string; // tiền tố của tiêu đề
|
||||||
|
title: string;
|
||||||
|
intro?: string;
|
||||||
|
brief?: string;
|
||||||
|
detail?: string;
|
||||||
|
summary?: string;
|
||||||
|
thumbnail?: string;
|
||||||
|
represent?: string;
|
||||||
|
keyword?: string;
|
||||||
|
description?: string;
|
||||||
|
code?: string; // slug
|
||||||
|
redirect?: string;
|
||||||
|
type?: number; // 1: Editorial, 2: General , 3: Reportage , 4: Interview , 5: Survey , 6: Tutorial , 7: Podcast, 8: Broadcast , 9: Talkshow , 10: Livestream , 11: Translation , 12: Promotion , 99: Other
|
||||||
|
layoutType?: number; // 1: Normal , 2: Short , 3: Long , 4: Page, 99: Other
|
||||||
|
contentType?: number; // 1: General , 2: Photo , 3: Audio , 4: Video , 5: Graphic , 6: Document , 7: Interaction, 99: Other
|
||||||
|
feature?: string; // Private, Exclusive, Featured, Sponsored ,Important ,Archived
|
||||||
|
interaction?: string; // user hobbies
|
||||||
|
language?: string; // vi, en, ...
|
||||||
|
otherSettings?: string;
|
||||||
|
views?: number;
|
||||||
|
likes?: number;
|
||||||
|
rates?: number;
|
||||||
|
follows?: number;
|
||||||
|
shares?: number;
|
||||||
|
reports?: number;
|
||||||
|
comments?: number;
|
||||||
|
otherStatistics?: string;
|
||||||
|
order?: number;
|
||||||
|
priority?: number;
|
||||||
|
tags?: string;
|
||||||
|
authors?: Author[];
|
||||||
|
topics?: string;
|
||||||
|
events?: string;
|
||||||
|
relations?: Article[];
|
||||||
|
categories?: string;
|
||||||
|
sourceTitle?: string;
|
||||||
|
sourceUrl?: string;
|
||||||
|
sourceType?: string;
|
||||||
|
status?: number;
|
||||||
|
} & Base;
|
||||||
|
|
||||||
|
export const listPaging = async (event: H3Event) => {
|
||||||
|
try {
|
||||||
|
const { apiUrl } = useRuntimeConfig().public;
|
||||||
|
const { categoryId, authorId, eventId, tagId, topicId, page, limit, sort } = getQuery(event)
|
||||||
|
const query = ref({})
|
||||||
|
if(eventId) {
|
||||||
|
query.value = { eventId }
|
||||||
|
}
|
||||||
|
if(categoryId) {
|
||||||
|
query.value = { categoryId }
|
||||||
|
}
|
||||||
|
if(authorId) {
|
||||||
|
query.value = { authorId }
|
||||||
|
}
|
||||||
|
if(tagId) {
|
||||||
|
query.value = { tagId }
|
||||||
|
}
|
||||||
|
if(topicId) {
|
||||||
|
query.value = { topicId }
|
||||||
|
}
|
||||||
|
const { items, total }: any = await $fetch(`${apiUrl}/cms/article-publishing/condition/paging:${page}-${limit}/sorting:${sort}`,{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {site: 1},
|
||||||
|
body:{ ...query.value }
|
||||||
|
})
|
||||||
|
|
||||||
|
return { items, total };
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listPagingWithTagId = async (event: H3Event) => {
|
||||||
|
try {
|
||||||
|
const { apiUrl } = useRuntimeConfig().public
|
||||||
|
const { tagId, page, limit, sort } = getQuery(event)
|
||||||
|
const { items, total }: any = await $fetch(`${apiUrl}/cms/article-publishing/condition/paging:${page}-${limit}/sorting:${sort}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
site: 1
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
tagId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return { items, total }
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// export const listPagingWithEventId = async (event: H3Event) => {
|
||||||
|
// try {
|
||||||
|
// const { apiUrl } = useRuntimeConfig().public
|
||||||
|
// const { eventId, page, limit, sort } = getQuery(event)
|
||||||
|
// const obj = { eventId }
|
||||||
|
// const { items, total }: any = await $fetch(`${apiUrl}/cms/article-publishing/condition/paging:${page}-${limit}/sorting:${sort}`,{
|
||||||
|
// method: 'POST',
|
||||||
|
// headers: {
|
||||||
|
// site: 1
|
||||||
|
// },
|
||||||
|
// body:{ ...obj }
|
||||||
|
// })
|
||||||
|
// return { items, total }
|
||||||
|
// } catch (error) {
|
||||||
|
// handleError(error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
export const listSectionArticle = async (event: H3Event) => {
|
||||||
|
try {
|
||||||
|
const { apiUrl } = useRuntimeConfig().public;
|
||||||
|
const { layout } = getQuery(event)
|
||||||
|
|
||||||
|
const { item }: any = await $fetch(`${apiUrl}/cms/category-section-article/category:41-sectionCode:${layout}`,{
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
return { item };
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const get = async (event: H3Event) => {
|
||||||
|
try {
|
||||||
|
const { apiUrl } = useRuntimeConfig().public;
|
||||||
|
const { articleId } = getQuery(event)
|
||||||
|
const { item }: any = await $fetch(`${apiUrl}/cms/article-publishing/code:${articleId}`, {
|
||||||
|
headers: {
|
||||||
|
Site: '1'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return item;
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { utils } from "~/utils/utilities";
|
||||||
|
import Base from "./base";
|
||||||
|
import { H3Event } from "h3";
|
||||||
|
|
||||||
|
export type Author = {
|
||||||
|
id:number;
|
||||||
|
siteId:number;
|
||||||
|
userId:number;
|
||||||
|
title:string
|
||||||
|
code:string;
|
||||||
|
description?:string;
|
||||||
|
thumbnail?:string;
|
||||||
|
feature?:string;
|
||||||
|
type?:number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchByCode = async (event: H3Event) => {
|
||||||
|
try {
|
||||||
|
const { apiUrl } = useRuntimeConfig().public
|
||||||
|
const { authorCode }: any = getQuery(event)
|
||||||
|
const { items }: any = await $fetch(`${apiUrl}/cms/author/code:${authorCode}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
site: 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return items[0]
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
import type { LocationQuery } from '#vue-router';
|
||||||
|
import type { PaginationInfo } from '~/objects';
|
||||||
|
import type { Article } from '~/server/models/article';
|
||||||
|
|
||||||
|
export const useArticleStoreV2 = defineStore('article-v2', () => {
|
||||||
|
|
||||||
|
const { serverResourceUrl, pagingDefault, pagingLimit } = useRuntimeConfig().public
|
||||||
|
|
||||||
|
const pagination = ref<PaginationInfo>({
|
||||||
|
page: utils.toNumber(pagingDefault),
|
||||||
|
limit: utils.toNumber(pagingLimit),
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchByCategoryIdWithPaging(categoryId: number, limit?: string | number) {
|
||||||
|
const { data, error } = await useFetch<any>(`/api/v2/articles-paging?categoryId=${categoryId}&page=${pagination.value.page}&limit=${limit ? limit : pagination.value.limit}&sort=createdon desc`)
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
pagination.value.total = 0;
|
||||||
|
return [] as Article[]
|
||||||
|
}
|
||||||
|
|
||||||
|
data.value.items.forEach((item: Article) => {
|
||||||
|
item.title = item.title.replace(/<p>|<\/p>/g, '')
|
||||||
|
})
|
||||||
|
|
||||||
|
pagination.value.total = data.value.total;
|
||||||
|
|
||||||
|
return data.value.items
|
||||||
|
}
|
||||||
|
|
||||||
|
// async function fetchByTagId(tagId: number, limit?: number, page?: number) {
|
||||||
|
// if(limit) {
|
||||||
|
// pagination.value.limit = limit;
|
||||||
|
// }
|
||||||
|
// if(page) {
|
||||||
|
// pagination.value.page = page;
|
||||||
|
// }
|
||||||
|
// const { data, error } = await useFetch<any>(`/api/v2/articles-tag`, {
|
||||||
|
// query: {
|
||||||
|
// tagId: tagId,
|
||||||
|
// limit: pagination.value.limit,
|
||||||
|
// page: pagination.value.page,
|
||||||
|
// sort: 'createdon desc'
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// if(error.value) {
|
||||||
|
// return null
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pagination.value.total = data.value.total
|
||||||
|
// return data.value.items
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async function fetchByTagId(tagId: number, limit?: number) {
|
||||||
|
// const { data, error } = await useFetch<any>(`/api/v2/articles-tag`, {
|
||||||
|
// query: {
|
||||||
|
// tagId: tagId,
|
||||||
|
// limit: limit ? limit : pagination.value.limit,
|
||||||
|
// page: pagination.value.page,
|
||||||
|
// sort: 'createdon desc'
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
// if(error.value) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pagination.value.total = data.value.total
|
||||||
|
|
||||||
|
// return data.value.items
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function fetchByCategorySectionArticle(layout: number) {
|
||||||
|
const { data, error } = await useFetch<any>(`/api/v2/articles-category-section?layout=${layout}`)
|
||||||
|
// if (error.value) {
|
||||||
|
// return {} as Article{}
|
||||||
|
// }
|
||||||
|
// data.value.items.forEach((item: Article) => {
|
||||||
|
// item.title = item.title.replace(/<p>|<\/p>/g, '')
|
||||||
|
// })
|
||||||
|
if(error.value) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
return data.value
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchByEventId(eventId: number, limit?: number) {
|
||||||
|
if(limit) {
|
||||||
|
pagination.value.limit = limit
|
||||||
|
}
|
||||||
|
const { data, error } = await useFetch<any>(`/api/v2/articles-paging`, {
|
||||||
|
query: {
|
||||||
|
eventId: eventId,
|
||||||
|
page: pagination.value.page,
|
||||||
|
limit: pagination.value.limit,
|
||||||
|
sort: 'createdon desc'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(error.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
pagination.value.total = data.value.total
|
||||||
|
return data.value.items
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchByAuthorId(authorId: number, limit?:number) {
|
||||||
|
if(limit) {
|
||||||
|
pagination.value.limit = limit
|
||||||
|
}
|
||||||
|
const { data, error } = await useFetch<any>(`/api/v2/articles-paging`, {
|
||||||
|
query: {
|
||||||
|
authorId: authorId,
|
||||||
|
page: pagination.value.page,
|
||||||
|
limit: pagination.value.limit,
|
||||||
|
sort: 'createdon desc'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(error.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
pagination.value.total = data.value.total
|
||||||
|
return data.value.items
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchByTopicId(topicId: number, limit?: number) {
|
||||||
|
if(limit) {
|
||||||
|
pagination.value.limit = limit
|
||||||
|
}
|
||||||
|
const { data, error } = await useFetch<any>(`/api/v2/articles-paging`, {
|
||||||
|
query: {
|
||||||
|
topicId: topicId,
|
||||||
|
page: pagination.value.page,
|
||||||
|
limit: pagination.value.limit,
|
||||||
|
sort: 'createdon desc'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(error.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
pagination.value.total = data.value.total
|
||||||
|
return data.value.items
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchByTagId(tagId: number, limit?: number) {
|
||||||
|
if(limit) {
|
||||||
|
pagination.value.limit = limit
|
||||||
|
}
|
||||||
|
const { data, error } = await useFetch<any>(`/api/v2/articles-paging`, {
|
||||||
|
query: {
|
||||||
|
tagId: tagId,
|
||||||
|
page: pagination.value.page,
|
||||||
|
limit: pagination.value.limit,
|
||||||
|
sort: 'createdon desc'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if(error.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
pagination.value.total = data.value.total
|
||||||
|
return data.value.items
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchById(id: string) {
|
||||||
|
const { data, error } = await useFetch<Article>(`/api/v2/articles`, { query: { articleId: id } })
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.value) {
|
||||||
|
data.value.title = data.value.title.replace(/<p>|<\/p>/g, '')
|
||||||
|
data.value.detail = utils.domainImage(data.value.detail, serverResourceUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStateFromRoute(query: LocationQuery) {
|
||||||
|
if (query.page) pagination.value.page = utils.toNumber(query.page);
|
||||||
|
else
|
||||||
|
pagination.value.page = utils.toNumber(
|
||||||
|
useRuntimeConfig().public.pagingDefault
|
||||||
|
);
|
||||||
|
if (query.limit) pagination.value.limit = utils.toNumber(query.limit);
|
||||||
|
else
|
||||||
|
pagination.value.limit = utils.toNumber(
|
||||||
|
useRuntimeConfig().public.pagingLimit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { pagination, fetchByTagId, fetchByCategoryIdWithPaging, fetchById, setStateFromRoute, fetchByCategorySectionArticle, fetchByEventId, fetchByTopicId, fetchByAuthorId }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept(acceptHMRUpdate(useArticleStoreV2, import.meta.hot))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user