diff --git a/components/dynamic-page/page-component/templates/other/details/emagazine.vue b/components/dynamic-page/page-component/templates/other/details/emagazine.vue
index 06965fa..b68c0d7 100644
--- a/components/dynamic-page/page-component/templates/other/details/emagazine.vue
+++ b/components/dynamic-page/page-component/templates/other/details/emagazine.vue
@@ -1,6 +1,5 @@
diff --git a/components/dynamic-page/page-component/templates/other/details/podcast.vue b/components/dynamic-page/page-component/templates/other/details/podcast.vue
new file mode 100644
index 0000000..cbdae7b
--- /dev/null
+++ b/components/dynamic-page/page-component/templates/other/details/podcast.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ utils.dateFormat(currentArticle?.createdOn, "dddd, DD/MM/YYYY - HH:mm") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/dynamic-page/page-component/templates/other/details/video.vue b/components/dynamic-page/page-component/templates/other/details/video.vue
new file mode 100644
index 0000000..189e6d8
--- /dev/null
+++ b/components/dynamic-page/page-component/templates/other/details/video.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
diff --git a/components/dynamic-page/page-component/templates/other/index.ts b/components/dynamic-page/page-component/templates/other/index.ts
index 62514c6..a03d302 100644
--- a/components/dynamic-page/page-component/templates/other/index.ts
+++ b/components/dynamic-page/page-component/templates/other/index.ts
@@ -2,6 +2,8 @@ export { default as Article_Button } from './copyLinks/ArticleButton.vue'
export { default as Article_Detail_Emagazine } from './details/emagazine.vue'
export { default as Article_Detail_Default } from './details/default.vue'
export { default as Article_Detail_Infographics } from './details/infographics.vue'
+export { default as Article_Detail_Podcast } from './details/podcast.vue'
+export { default as Article_Detail_Video } from './details/video.vue'
export { default as Default_Breadcrumb} from './breadcrumb/default.vue'
export { default as ADS_Default } from './ads/default.vue';
export { default as Comment } from './comments/default.vue'
\ No newline at end of file
diff --git a/components/dynamic-page/page-component/templates/other/index.vue b/components/dynamic-page/page-component/templates/other/index.vue
index effdbf5..c4a213b 100644
--- a/components/dynamic-page/page-component/templates/other/index.vue
+++ b/components/dynamic-page/page-component/templates/other/index.vue
@@ -1,7 +1,8 @@
+
+
+
+
+
+
+
+
+ {{ currrentTimeComputed }}
+ {{ durationComputed }}
+
+
+
+
+
+ Tốc độ phát {{
+ speedDefault
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index b9d2f84..f2d0de7 100644
--- a/package.json
+++ b/package.json
@@ -5,56 +5,34 @@
"build": "nuxt build --dotenv .env.production",
"dev": "nuxt dev",
"generate": "nuxt generate",
- "preview": "nuxt preview",
- "postinstall": "nuxt prepare"
+ "preview": "nuxt preview"
},
"devDependencies": {
"@ant-design-vue/nuxt": "^1.4.1",
"@nuxt/devtools": "1.0.0",
"@nuxt/image": "^1.1.0",
"@pinia/nuxt": "latest",
- "@remix-run/web-file": "^3.1.0",
- "@sidebase/nuxt-auth": "^0.6.4",
- "@types/glidejs__glide": "latest",
"@unocss/postcss": "latest",
- "cva": "beta",
"dayjs-nuxt": "^2.1.8",
"nuxt": "latest",
- "nuxt-custom-elements": "beta",
- "nuxt-headlessui": "^1.1.4",
"nuxt-icon": "latest",
"nuxt-lodash": "latest",
"typescript": "^5.3.3",
"vue-tsc": "^1.8.27"
},
"dependencies": {
- "@glidejs/glide": "^3.6.0",
+ "@types/lodash": "^4.17.4",
"@unocss/nuxt": "latest",
"@unocss/reset": "latest",
"@vueuse/core": "^10.8.0",
"@vueuse/nuxt": "10.5.0",
- "aos": "latest",
"axios": "^1.5.1",
"cheerio": "^1.0.0-rc.12",
- "clsx": "^2.1.0",
- "defu": "^6.1.4",
- "ipx": "^3.0.1",
- "mitt": "^3.0.1",
- "next-auth": "4.21.1",
- "node-html-parser": "latest",
+ "lodash": "^4.17.21",
"nuxt-delay-hydration": "latest",
- "nuxt-swiper": "latest",
- "parse-nested-form-data": "^1.0.0",
- "request": "^2.88.2",
- "request-promise": "^0.0.1",
- "require": "^0.4.4",
"sass": "latest",
"sass-loader": "latest",
- "tailwind-merge": "latest",
- "vite-svg-loader": "latest",
- "vue-advanced-cropper": "^2.8.8",
- "winston": "^3.11.0",
- "zod": "^3.22.4"
+ "tailwind-merge": "latest"
},
"overrides": {
"vue": "latest"
diff --git a/pages/bai-viet/[slug].vue b/pages/bai-viet/[slug].vue
index e4148a7..863cd67 100644
--- a/pages/bai-viet/[slug].vue
+++ b/pages/bai-viet/[slug].vue
@@ -16,6 +16,7 @@ import { useDynamicPageStore } from '~/stores/dynamic-page';
import { useArticleStore } from '~/stores/articles';
const { currentPage, sectionPublished, componentPublished } = storeToRefs(useDynamicPageStore());
const { currentArticle } = storeToRefs(useArticleStore());
+
const store = reactive({
dynamicPage: useDynamicPageStore(),
article: useArticleStore(),
@@ -37,7 +38,6 @@ const loadPage = async (contentType: string | number) => {
watch(currentArticle, async () => {
let isContentType : string = '';
- console.log(currentArticle.value, 'type')
switch (currentArticle.value?.contentType) {
case 1:
isContentType = 'trang-doi-song'
@@ -47,11 +47,11 @@ watch(currentArticle, async () => {
break;
case 3:
- isContentType = 'ArticleLayoutPodcast'
+ isContentType = 'trang-chi-tiet-podcast'
break;
case 4:
- isContentType = 'ArticleLayoutVideo'
+ isContentType = 'trang-chi-tiet-video-clip'
break;
case 5:
@@ -71,15 +71,16 @@ watch(currentArticle, async () => {
isContentType = 'trang-chi-tiet-emagazine'
break;
}
- await loadPage(isContentType)
+ await loadPage(isContentType);
}, { deep: true })
useSeoMeta({
- title: currentArticle.value?.title?.replace(/<[^>]+>/g, ''),
- ogTitle: currentArticle.value?.title,
- description: currentArticle.value?.intro,
- ogDescription: currentArticle.value?.intro,
- ogImage: currentArticle.value?.thumbnail,
+ title: () => currentArticle.value?.title?.replace(/<[^>]+>/g, ''),
+ description: () => currentArticle.value?.intro,
+ ogTitle: () => currentArticle.value?.title,
+ ogImage: () => currentArticle.value?.thumbnail,
+ ogDescription: () => currentArticle.value?.intro,
+ twitterCard: 'summary_large_image',
})
diff --git a/pages/index.vue b/pages/index.vue
index 2127d92..3fbe9c0 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -3,7 +3,6 @@ import _cloneDeep from 'lodash/cloneDeep';
import DynamicTemplate from "~/components/dynamic-page/page/templates/index.vue";
import DynamicSection from "~/components/dynamic-page/page-section/templates/index.vue";
-
import { useDynamicPageStore } from '~/stores/dynamic-page';
const { currentPage, sectionPublished, componentPublished } = storeToRefs(useDynamicPageStore());
diff --git a/server/api/services/[...].ts b/server/api/services/[...].ts
index 637397f..491a43c 100644
--- a/server/api/services/[...].ts
+++ b/server/api/services/[...].ts
@@ -1,8 +1,14 @@
import { createRouter, defineEventHandler, useBase } from 'h3'
import * as navigationCtrl from '~/server/models/navigation'
+import * as eventCtrl from '~/server/models/event'
+import * as tagCtrl from '~/server/models/tag'
+import * as topicCtrl from '~/server/models/topic'
const router = createRouter()
router.get('/navigation', defineEventHandler(navigationCtrl.get))
+router.get('/tag', defineEventHandler(tagCtrl.fetchById))
+router.get('/topic', defineEventHandler(topicCtrl.fetchById))
+router.get('/event', defineEventHandler(eventCtrl.fetchById))
export default useBase('/api/services', router.handler)
diff --git a/server/models/author.ts b/server/models/author.ts
new file mode 100644
index 0000000..8280af4
--- /dev/null
+++ b/server/models/author.ts
@@ -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)
+ }
+}
\ No newline at end of file
diff --git a/server/models/event.ts b/server/models/event.ts
new file mode 100644
index 0000000..b5336b3
--- /dev/null
+++ b/server/models/event.ts
@@ -0,0 +1,50 @@
+import { utils } from "~/utils/utilities";
+import Base from "./base";
+import { H3Event } from "h3";
+
+export const listPaging = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { siteId, page, fetch } = getQuery(event)
+
+ const { items, total }: any = await $fetch(`${apiUrl}/cms/event/condition/paging:${page}-${fetch}`, {
+ method: 'POST',
+ body: {siteIds: [siteId]}
+ })
+ return {items, total}
+ } catch (error) {
+ handleError(error);
+ }
+}
+
+export const fetchByCode = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { eventCode }: any = getQuery(event)
+ const { item }: any = await $fetch(`${apiUrl}/cms/event/code:${eventCode}`, {
+ method: 'GET',
+ headers: {
+ Site: 1
+ }
+ })
+ return item
+ } catch (error) {
+ handleError(error)
+ }
+}
+
+export const fetchById = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { eventId }: any = getQuery(event)
+ const { item }: any = await $fetch(`${apiUrl}/cms/event/${eventId}`, {
+ method: 'GET',
+ headers: {
+ Site: 1
+ }
+ })
+ return item
+ } catch (error) {
+ handleError(error)
+ }
+}
diff --git a/server/models/tag.ts b/server/models/tag.ts
new file mode 100644
index 0000000..e3b81b0
--- /dev/null
+++ b/server/models/tag.ts
@@ -0,0 +1,36 @@
+import { utils } from "~/utils/utilities";
+import { Author } from "./author";
+import Base from "./base";
+import { H3Event } from "h3";
+
+export const get = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { code } = getQuery(event)
+
+ const { items }: any = await $fetch(`${apiUrl}/cms/tag/code:${code}`, {
+ headers: {
+ site: 1
+ }
+ })
+ return items
+ } catch(error) {
+ handleError(error);
+ }
+}
+
+export const fetchById = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { tagId }: any = getQuery(event)
+ const { item }: any = await $fetch(`${apiUrl}/cms/tag/${tagId}`, {
+ method: 'GET',
+ headers: {
+ Site: 1
+ }
+ })
+ return item
+ } catch (error) {
+ handleError(error)
+ }
+}
\ No newline at end of file
diff --git a/server/models/topic.ts b/server/models/topic.ts
new file mode 100644
index 0000000..2a6392e
--- /dev/null
+++ b/server/models/topic.ts
@@ -0,0 +1,60 @@
+import { utils } from "~/utils/utilities";
+import { Author } from "./author";
+import Base from "./base";
+import { ref } from "vue"
+import { H3Event } from "h3";
+
+export const listPaging = async (event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { categoryId, page, limit, sort } = getQuery(event)
+ const query = ref({})
+ if(categoryId) {
+ query.value = { categoryId }
+ }
+
+ const { items, total }: any = await $fetch(`${apiUrl}/cms/topic/condition/paging:${page}-${limit}/sorting:${sort}`,{
+ method: 'POST',
+ headers: {site: 1},
+ body:{ ...query.value }
+ })
+
+ return { items, total };
+ } catch (error) {
+ handleError(error)
+ }
+}
+
+export const fetchByCode = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { topicCode }: any = getQuery(event)
+ const { item }: any = await $fetch(`${apiUrl}/cms/topic/code:${topicCode}`, {
+ method: 'GET',
+ headers: {
+ site: 1
+ }
+ })
+
+ return item
+ } catch (error) {
+ handleError(error)
+ }
+}
+
+export const fetchById = async(event: H3Event) => {
+ try {
+ const { apiUrl } = useRuntimeConfig().public
+ const { topicId }: any = getQuery(event)
+ const { item }: any = await $fetch(`${apiUrl}/cms/topic/${topicId}`, {
+ method: 'GET',
+ headers: {
+ site: 1
+ }
+ })
+
+ return item
+ } catch (error) {
+ handleError(error)
+ }
+}
\ No newline at end of file
diff --git a/stores/articles.ts b/stores/articles.ts
index 1853b64..388ea34 100644
--- a/stores/articles.ts
+++ b/stores/articles.ts
@@ -12,9 +12,9 @@ export const useArticleStore = defineStore("article", () => {
const getArticleBySlug = async (slug: string) => {
try {
- const { data} = await useFetch(`/api/articles/get-by-slug/${slug}`)
+ const { data: article } = await useAsyncData('article', () => $fetch(`/api/articles/get-by-slug/${slug}`))
currentArticle.value = {}
- currentArticle.value = data.value.item
+ currentArticle.value = article.value?.item
} catch (error: any) {}
}
diff --git a/stores/event.ts b/stores/event.ts
new file mode 100644
index 0000000..d2d394f
--- /dev/null
+++ b/stores/event.ts
@@ -0,0 +1,45 @@
+import { LocationQuery } from "vue-router";
+import { defineStore, acceptHMRUpdate } from "pinia";
+
+export const useEventStore = defineStore('event', () => {
+
+ const pagination = ref({
+ page: utils.toNumber(useRuntimeConfig().public.pagingDefault),
+ limit: utils.toNumber(useRuntimeConfig().public.pagingLimit),
+ total: 0,
+ });
+
+ 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);
+ }
+
+ async function listPaging(siteId: number, page: number, fetch: number) {
+ const { data, error } = await useFetch
(`/api/services/events-paging?siteId=${siteId}&page=${page}&fetch=${fetch}`)
+ if (error.value) {
+ return null
+ }
+ return data.value
+ }
+
+ async function fetchById(id: string) {
+ const { data, error } = await useFetch(`/api/services/event`, {
+ query: {
+ eventId: id
+ }
+ })
+ if(error.value) {
+ return null
+ }
+
+ return data.value
+ }
+
+ return { listPaging, fetchById, setStateFromRoute, pagination }
+})
+
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useEventStore, import.meta.hot))
+}
\ No newline at end of file
diff --git a/stores/tag.ts b/stores/tag.ts
new file mode 100644
index 0000000..b35c53c
--- /dev/null
+++ b/stores/tag.ts
@@ -0,0 +1,28 @@
+export const useTagStore = defineStore('tag', () => {
+ // async function fetchByCode(code: string) {
+ // const { data, error } = await useFetch(`/api/services/tag`, { query: {code: code}})
+ // if(error.value) {
+ // return null
+ // }
+ // return data.value[0]
+ // }
+
+ async function fetchById(id: string) {
+ const { data, error } = await useFetch(`/api/services/tag`, {
+ query: {
+ tagId: id
+ }
+ })
+ if(error.value) {
+ return null
+ }
+
+ return data.value
+ }
+
+ return { fetchById }
+})
+
+if(import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useTagStore, import.meta.hot))
+}
\ No newline at end of file
diff --git a/stores/topic.ts b/stores/topic.ts
new file mode 100644
index 0000000..44342c1
--- /dev/null
+++ b/stores/topic.ts
@@ -0,0 +1,55 @@
+import { LocationQuery } from "vue-router";
+import { defineStore, acceptHMRUpdate } from "pinia";
+
+export const useTopicStore = defineStore('topic', () => {
+ const pagination = ref({
+ page: utils.toNumber(useRuntimeConfig().public.pagingDefault),
+ limit: utils.toNumber(useRuntimeConfig().public.pagingLimit),
+ total: 0,
+ });
+
+ 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);
+ }
+
+ async function fetchById(topicId: string) {
+ const { data, error } = await useFetch(`/api/services/topic`, {
+ query: {
+ topicId: topicId
+ }
+ })
+ if(error.value) {
+ return null
+ }
+
+ return data.value
+ }
+
+ async function fetchByCategoryId(categoryId: number, limit?: number) {
+ if(limit) {
+ pagination.value.limit = limit
+ }
+ const { data, error } = await useFetch(`/api/services/topics-paging`, {
+ query: {
+ categoryId: categoryId,
+ limit: pagination.value.limit,
+ page: pagination.value.page,
+ sort: 'createdon desc'
+ }
+ })
+ if(error.value) {
+ return null
+ }
+
+ return data.value.items
+ }
+
+ return { pagination, setStateFromRoute, fetchById, fetchByCategoryId }
+})
+
+if (import.meta.hot) {
+ import.meta.hot.accept(acceptHMRUpdate(useTopicStore, import.meta.hot))
+}
\ No newline at end of file
diff --git a/uno.config.ts b/uno.config.ts
index b21fc4c..dab8d87 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -70,16 +70,11 @@ export default defineConfig({
extractors: [],
presets: [
presetUno(),
- presetMini(),
presetWebFonts({
provider: "google",
fonts: {
nunito: "Nunito",
playfair: ['Playfair Display', 'sans-serif'],
- 'playfair-display': ['Playfair Display', 'serif'],
- 'bai-jamjuree': ['Bai Jamjuree', 'Arial', 'sans-serif'],
- sans: ['Raleway', 'Arial', "Helvetica Neue", 'Helvetica', 'sans-serif'],
- arial: ['Arial', 'sans-serif'],
},
}),
],
diff --git a/utils/parseSQL.ts b/utils/parseSQL.ts
index 692a0bc..597d177 100644
--- a/utils/parseSQL.ts
+++ b/utils/parseSQL.ts
@@ -1,9 +1,4 @@
-/* Bộ query mẫu */
-// Sql[SELECT * FROM Table WHERE Id=1] Key[xxx]
-// Uri[link-api] Method[Post] Params[{"param1":"value1","param2":"value2"}] Headers[{"Authorization":"12345678","Content-Type":"application/json"}] Content[{"data1":"value1","data2":"value2"}] Key[xxx]
-// Get[Article] Top[10] With[Topics:1,2,3] Sort[Views-,Shares+]
-
-import { isEmpty } from "lodash";
+import isEmpty from "lodash/isEmpty";
const keyMapping = {
// 3 query key để phân loại