thainnv-dev: Nhúng

This commit is contained in:
nguyen van thai
2024-06-06 13:29:22 +07:00
parent 0a7774b7f4
commit c217ed82c9
25 changed files with 382 additions and 45 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ img.wide {
margin-left: 0;
max-width: 70%;
width: 70%;
transform: translateX(20%);
/* transform: translateX(20%); */
}
figure.image.wide {
+18 -8
View File
@@ -11,17 +11,27 @@
figure {
margin: auto !important;
}
.content {
& p {
@apply mb-2;
}
& audio {
@apply w-full;
}
& a {
@apply text-primary-600 underline;
}
& document {
@apply cursor-pointer text-primary-600 underline;
}
}
div[layout="ARTICLE_DETAIL_EMAGAZINE"] {
& p,& figure.align-center-image, & #sub, & #title, & #intro {
@apply lg:max-w-640px mx-auto;
}
// @at-root .section_layout{
// & div[layout="BREADCRUM_DEFAULT"] {
// @apply lg:max-w-640px mx-auto;
// }
// }
}
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { useArticleStore } from '~/stores/articles';
const { currentArticle } = storeToRefs(useArticleStore());
const props = defineProps<{
dataId?: string,
}>()
const store = reactive({
article: useArticleStore()
})
// onBeforeMount(async () => {
// await store.article.getArticleById(Number(props.dataId))
// })
</script>
<template>
12
<!-- {{ currentArticle }} 12 -->
<!-- <a href="#" :style="style" class="!no-underline !px-2">{{ title }} 1</a> -->
</template>
+14
View File
@@ -0,0 +1,14 @@
<script setup lang="ts">
const props = defineProps<{
dataResource?: string,
dataTitle?: string,
}>()
const resource = computed(() => props.dataResource ?? '')
const title = computed(() => props.dataTitle ?? '')
const fileName = computed(() => `${title.value}.${resource.value.split('.').pop()}`)
</script>
<template>
<a :href="resource" :download="fileName">{{fileName}}</a>
</template>
+14
View File
@@ -0,0 +1,14 @@
<script setup lang="ts">
const props = defineProps<{
dataResource?: string,
dataTitle?: string,
}>()
const resource = computed(() => props.dataResource ?? '')
const title = computed(() => props.dataTitle ?? '')
const fileName = computed(() => `${title.value}.${resource.value.split('.').pop()}`)
</script>
<template>
<a :href="resource" :download="fileName">{{fileName}}</a>
</template>
+56
View File
@@ -0,0 +1,56 @@
<script setup lang="ts">
import { usePollStore } from '~/stores/poll'
import { usePollOptionStore } from '~/stores/poll-option'
import type { Poll } from '~/server/models/poll';
const props = defineProps<{ dataId?: string }>();
const store = reactive({
poll: usePollStore(),
pollOptions: usePollOptionStore()
})
const poll = reactive<Poll>(await store.poll.fetchById((String(props.dataId))))
const options = ref<any []>(await store.pollOptions.fetchByPollId((String(props.dataId))))
const selectedOption = ref<any>(null);
function submitVote () {
console.log(selectedOption, 'id')
}
</script>
<template>
<span class="inline-block px-4 py-2 shadow-xl rounded-lg bg-[#f5f5f5]">
<span class="block">
<span class="underline decoration-gray-500 font-bold">
{{ poll?.title }}
</span>
<!-- <button v-if="showResult && canShowResult" type="button" class="underline text-blue-400" @click="toggleResults">
Câu hỏi
</button>
<button class="underline text-blue-400" v-if="!showResult && canShowResult" type="button" @click="toggleResults">
Kết quả
</button> -->
</span>
<span class="p-1 block">
<span v-for="(option, index) in options" :key="index" class="block">
<label class="flex gap-2 m-2">
<input type="radio" :value="option.id" v-model="selectedOption" />
<span class="font-semibold">{{ option?.title }}</span>
</label>
</span>
<button @click="submitVote" class="bg-primary-500 text-white py-1 px-3 rounded-4px cursor-pointer hover:bg-primary-600 float-right">Bình chọn </button>
</span>
<!-- <span v-else class="block">
<span v-for="(answer, index) in options" :key="index" class="block poll-result relative rounded-3xl overflow-hidden my-3">
<span class="absolute top-0 start-0 bottom-0 bg-gradient-to-r from-sky-500 to-indigo-500"
:style="{ width: `${calculatePercentage(answer?.voteCount)}%` }"></span>
<span class="block relative z-0 ps-1">
<span>{{ calculatePercentage(answer?.voteCount).toFixed(2) }}%</span>
<span class="">({{ answer?.voteCount }})</span>
</span>
</span>
</span> -->
</span>
</template>
+6
View File
@@ -0,0 +1,6 @@
<script setup lang="ts">
</script>
<template>
quiz
</template>
+6
View File
@@ -0,0 +1,6 @@
<script setup lang="ts">
</script>
<template>
Survey
</template>
+15
View File
@@ -0,0 +1,15 @@
<script setup lang="ts">
const props = defineProps<{
dataCode?: string,
dataTitle?: string,
style?: string
}>()
const title = computed(() => props.dataTitle ?? '')
const code = computed(() => props.dataCode ?? '')
const style = computed(() => props.style ?? '')
</script>
<template>
<a href="#" :style="style" class="!no-underline !px-2">{{ title }} 1</a>
</template>
@@ -2,30 +2,40 @@
import { useDynamicPageStore } from "~/stores/dynamic-page";
if(!localStorage.getItem('step')) {
localStorage.setItem('step', 0)
localStorage.setItem('step', '0')
}
useDynamicPageStore().step = localStorage.getItem('step') ?? 0
useDynamicPageStore().step = Number(localStorage.getItem('step')) ?? 0
function increase() {
useDynamicPageStore().step = Number(useDynamicPageStore().step) + 2
localStorage.setItem('step', useDynamicPageStore().step)
localStorage.setItem('step', useDynamicPageStore().step.toString())
}
function decrease() {
useDynamicPageStore().step = Number(useDynamicPageStore().step) - 2
localStorage.setItem('step', useDynamicPageStore().step)
localStorage.setItem('step', useDynamicPageStore().step.toString())
}
onMounted(() => {
let detailEmagazine = document.querySelector('div[layout="ARTICLE_DETAIL_EMAGAZINE"]')
let breakcrumb = document.querySelector('div[layout="BREADCRUM_DEFAULT"]')
if( detailEmagazine && breakcrumb) {
breakcrumb.classList.add('lg:max-w-640px','mx-auto')
}
console.log('b')
})
</script>
<template>
<div class="flex justify-between items-center">
<div class="flex justify-between items-center my-3">
<ul class="flex gap-6 items-center text-md">
<li class="first:text-primary-600 font-semibold">
<nuxt-link to="/">Trang chủ</nuxt-link>
</li>
</ul>
<div class="flex gap-1.5">
<div class="flex gap-2">
<div @click="increase()" class="w-10 h-10 border-1px border-solid shadow rounded-full relative cursor-pointer hover:bg-primary-100 hover:text-primary-600">
<svg class="absolute top-50% left-50% translate-y--50% translate-x--55%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path fill="none" d="M0 0h24v24H0z"></path><path d="M11.246 15H4.75416L2.75416 20H0.600098L7.0001 4H9.0001L15.4001 20H13.246L11.246 15ZM10.446 13L8.0001 6.88516L5.55416 13H10.446ZM21.0001 12.5351V12H23.0001V20H21.0001V19.4649C20.4118 19.8052 19.7287 20 19.0001 20C16.791 20 15.0001 18.2091 15.0001 16C15.0001 13.7909 16.791 12 19.0001 12C19.7287 12 20.4118 12.1948 21.0001 12.5351ZM19.0001 18C20.1047 18 21.0001 17.1046 21.0001 16C21.0001 14.8954 20.1047 14 19.0001 14C17.8955 14 17.0001 14.8954 17.0001 16C17.0001 17.1046 17.8955 18 19.0001 18Z"></path></svg>
<svg class="absolute right-1.5 top-2 w-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>
@@ -4,21 +4,59 @@ import { useWindowSize } from '@vueuse/core'
const { width } = useWindowSize()
import { useArticleStore } from '~/stores/articles';
import { useDynamicPageStore } from "~/stores/dynamic-page";
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'
import Articlerelation from '~/components/article/immerse/ArticleRelation.vue'
const { currentArticle } = storeToRefs(useArticleStore());
const { step } = storeToRefs(useDynamicPageStore());
import * as cherrio from 'cheerio'
const $ = cherrio.load(currentArticle.value.detail)
// console.log($, 'cherrip')
// onBeforeMount(async () => {
// await useArticleStore().getArticleCondition({ids: [1, 2, 3]})
// })
// onMounted(() => {
// const documentElements = document.querySelectorAll('document, attachment')
// console.log(documentElements, 'doc')
// if(documentElements.length > 0) {
// documentElements.forEach((doc) => {
// doc.addEventListener('click', (event) => {
// event.preventDefault();
// const url = doc.getAttribute('data-resource');
// const file = doc.getAttribute('data-resource') ? doc.getAttribute('data-resource')?.toString().split('.').pop() : 'docx'
// fileName.value = `${doc.getAttribute('data-title')}.${file}` ;
// console.log(url , fileName.value, '123')
// if(url && fileName.value) {
// const a = document.createElement('a');
// a.href = url?.toString();
// a.download = fileName.value;
// document.body.appendChild(a);
// a.click();
// document.body.removeChild(a);
// }
// })
// })
// }
// })
</script>
<template>
<div class="content" v-if="currentArticle">
<h1 id="sub" v-html="currentArticle?.sub" class=" font-bold opacity-60 pb-1" :style="{ 'font-size': `${16 + Number(step)}px`}"></h1>
<h3 id="title" :style="{ 'font-size': width > breakpoint.lg ? `${36 + Number(step)}px` : `${20 + Number(step)}px`}" class="font-bold pb-1" v-html="currentArticle?.title"></h3>
<p id="published-on" class="text-gray-600" :style="{ 'font-size': `${14 + Number(step)}px` }">{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}</p>
<div id="intro" v-if="currentArticle?.intro" v-html="currentArticle?.intro" class="font-semibold tracking-widest pb-1" :style="{'font-size': `${16 + Number(step)}px`}"></div>
<div id="article-detail" :class="'tracking-wider'" v-html="currentArticle.detail" class="[&_img]:mx-auto" :style="{ 'font-size': `${16 + Number(step)}px`}"> </div>
<h3 id="title" :style="{ 'font-size': width > breakpoint.lg ? `${32 + Number(step)}px` : `${20 + Number(step)}px`}" class="font-bold pb-1" v-html="currentArticle?.title"></h3>
<p id="published-on" class="text-gray-600 mb-3" :style="{ 'font-size': `${14 + Number(step)}px` }">{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}</p>
<div id="intro" v-if="currentArticle?.intro" v-html="currentArticle?.intro" class="font-semibold tracking-widest pb-1 mb-3" :style="{'font-size': `${16 + Number(step)}px`}"></div>
<!-- <div id="article-detail" :class="'tracking-wider'" v-html="currentArticle.detail" class="[&_img]:mx-auto" :style="{ 'font-size': `${16 + Number(step)}px`}"> </div> -->
<component :is="{template: currentArticle.detail, components: { Poll, Document, Attachment, Tag} }" />
<!-- <component :is="{template: currentArticle.detail, components: { Poll, Quiz, Survey, Document, Attachment, Tag} }" /> -->
</div>
</template>
@@ -12,10 +12,10 @@ const { step } = storeToRefs(useDynamicPageStore());
<template>
<div class="content" v-if="currentArticle">
<h1 id="sub" v-html="currentArticle?.sub" class=" font-bold opacity-60 pb-1" :style="{ 'font-size': `${16 + Number(step)}px`}"></h1>
<h3 id="title" :style="{ 'font-size': width > breakpoint.lg ? `${36 + Number(step)}px` : `${20 + Number(step)}px`}" class="font-bold pb-1" v-html="currentArticle?.title"></h3>
<p id="published-on" class="text-gray-600" :style="{ 'font-size': `${14 + Number(step)}px` }">{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}</p>
<h3 id="title" :style="{ 'font-size': width > breakpoint.lg ? `${32 + Number(step)}px` : `${20 + Number(step)}px`}" class="font-bold pb-1" v-html="currentArticle?.title"></h3>
<p id="published-on" class="text-gray-600 mb-3" :style="{ 'font-size': `${14 + Number(step)}px` }">{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}</p>
<div id="intro" v-if="currentArticle?.intro" v-html="currentArticle?.intro" class="font-semibold tracking-widest pb-1" :style="{'font-size': `${16 + Number(step)}px`}"></div>
<div id="intro" v-if="currentArticle?.intro" v-html="currentArticle?.intro" class="font-semibold tracking-widest pb-1 mb-3" :style="{'font-size': `${16 + Number(step)}px`}"></div>
<div id="article-detail" :class="' tracking-wider'" v-html="currentArticle.detail" class="[&_img]:mx-auto" :style="{ 'font-size': `${16 + Number(step)}px`}"> </div>
</div>
</template>
@@ -12,11 +12,11 @@ const { step } = storeToRefs(useDynamicPageStore());
<template>
<div class="content" v-if="currentArticle">
<h1 id="sub" v-html="currentArticle?.sub" class=" font-bold opacity-60 pb-1" :style="{ 'font-size': `${16 + Number(step)}px`}"></h1>
<h3 id="title" :style="{ 'font-size': width > breakpoint.lg ? `${36 + Number(step)}px` : `${20 + Number(step)}px`}" class="font-bold pb-1" v-html="currentArticle?.title"></h3>
<p id="published-on" class="text-gray-600" :style="{ 'font-size': `${14 + Number(step)}px` }">{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}</p>
<h3 id="title" :style="{ 'font-size': width > breakpoint.lg ? `${32 + Number(step)}px` : `${20 + Number(step)}px`}" class="font-bold pb-1" v-html="currentArticle?.title"></h3>
<p id="published-on" class="text-gray-600 mb-3" :style="{ 'font-size': `${14 + Number(step)}px` }">{{ utils.dateFormat(currentArticle?.publishedOn, "dddd, DD/MM/YYYY - HH:mm") }}</p>
<!-- <div class="author">Tác giả - Thời gian tạo</div> -->
<div id="intro" v-if="currentArticle?.intro" v-html="currentArticle?.intro" class="font-semibold tracking-widest pb-1" :style="{'font-size': `${16 + Number(step)}px`}"></div>
<div id="intro" v-if="currentArticle?.intro" v-html="currentArticle?.intro" class="font-semibold tracking-widest mb-3" :style="{'font-size': `${16 + Number(step)}px`}"></div>
<div id="article-detail" :class="'text-[16px] tracking-wider'" v-html="currentArticle.detail" class="[&_img]:mx-auto" :style="{ 'font-size': `${16 + Number(step)}px`}"> </div>
</div>
@@ -84,25 +84,11 @@ watch(
</template>
<div class="button-page flex">
<a class="btn-page prev-page">
<i class="el-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path
fill="currentColor"
d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
></path>
</svg>
</i>
<Icon name="material-symbols:arrow-back-ios-new-rounded"/>
</a>
<a class="btn-page" @click="() => select(index + 1)" v-for="(_, index) in totals" :key="index">{{ index + 1 }}</a>
<a class="btn-page next-page">
<i class="el-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<path
fill="currentColor"
d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z"
></path>
</svg>
</i>
<Icon class="rotate-180deg" name="material-symbols:arrow-back-ios-new-rounded"/>
</a>
</div>
</div>
@@ -164,6 +150,7 @@ watch(
display: flex;
justify-content: center;
align-items: center;
color:#409eff;
}
}
.el-empty {
+4
View File
@@ -82,6 +82,10 @@ useSeoMeta({
ogDescription: () => currentArticle.value?.intro,
twitterCard: 'summary_large_image',
})
onMounted(() => {
console.log(document.querySelectorAll('document'), '12')
})
</script>
<template>
+2
View File
@@ -28,6 +28,8 @@ watch(currentPage, () => {
useHead({
title: 'Trang chủ'
})
</script>
<template>
+6 -1
View File
@@ -3,6 +3,9 @@ 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'
import * as pollCtrl from '~/server/models/poll'
import * as pollOptionCtrl from '~/server/models/poll-option'
import * as pollResponseCtrl from '~/server/models/poll-response'
const router = createRouter()
@@ -10,5 +13,7 @@ router.get('/navigation', defineEventHandler(navigationCtrl.get))
router.get('/tag', defineEventHandler(tagCtrl.fetchById))
router.get('/topic', defineEventHandler(topicCtrl.fetchById))
router.get('/event', defineEventHandler(eventCtrl.fetchById))
router.get('/poll-by-id', defineEventHandler(pollCtrl.fetchById))
router.get('/poll-option/pollId', defineEventHandler(pollOptionCtrl.fetchByPollId))
router.post('/poll-response', defineEventHandler(pollResponseCtrl.create))
export default useBase('/api/services', router.handler)
+1 -1
View File
@@ -94,7 +94,7 @@ export const listArticleCondition = async (event: H3Event) => {
try {
const payload = await readBody<any>(event)
const { apiUrl } = useRuntimeConfig().public
const { items }: any = await $fetch(`${apiUrl}/cms/article/condition`, {
const { items }: any = await $fetch(`${apiUrl}/cms/digital-article/condition`, {
method: "POST",
headers: {
Site: 1
+1 -1
View File
@@ -101,7 +101,7 @@ export const getDynamicPageByCode = async (event : any) => {
try {
const { apiUrl } = useRuntimeConfig().public
const slug = event.context.params.slug;
const { item }: any = await $fetch(`${apiUrl}/cms/overview-page/slug:${slug}`, {
const { item }: any = await $fetch(`${apiUrl}/cms/page/overview-page/slug:${slug}`, {
headers: new Headers({
site: '1' || 1,
}),
+22
View File
@@ -0,0 +1,22 @@
import { H3Event } from 'h3';
import Base from './base'
export const fetchByPollId = async (event: H3Event) => {
try {
const { apiUrl } = useRuntimeConfig().public
const { pollId}: any = getQuery(event)
const { items }: any = await $fetch(`${apiUrl}/cms/poll-option/poll:${pollId}`, {
method: 'GET',
headers: {
site: 1
}
})
return items
} catch (error) {
handleError(error);
}
}
+24
View File
@@ -0,0 +1,24 @@
import { H3Event } from 'h3';
import Base from './base'
export const create = async (event: H3Event) => {
try {
const { apiUrl } = useRuntimeConfig().public
const payload = await readBody<any>(event)
const { item }: any = await $fetch(`${apiUrl}/cms/poll-response`, {
method: 'POST',
headers: {
site: 1
},
body: {
payload
}
})
return item
} catch (error) {
handleError(error);
}
}
+43
View File
@@ -0,0 +1,43 @@
import { H3Event } from 'h3';
import Base from './base'
export type Poll = {
id?: number; // Mã định danh
siteId?: number; // Mã hệ thống
title?: string; // Tiêu đề
code?: string; // Nhận diện
keywords?: string; // Từ khóa
thumbnail?: string; // Ảnh nhỏ
description?: string; // Mô tả
startTime?: string; // Bắt đầu (string)
endTime?: string; // Kết thúc (string)
type?: number; // Phân loại
settings?: object; // Thiết lập: PollSettings (Json)
features?: string; // Đặc trưng: Featured (nổi bật)
taxonomy?: string; // Phân nhóm
isPublished?: boolean; // Đã xuất bản
publishedBy?: number; // Xuất bản bởi
publishedOn?: string; // Xuất bản vào lúc (string)
expiresOn?: string; // Đã hết hạn vào lúc (string)
order?: number; // Thứ tự sắp xếp
status?: number; // Trạng thái
}
export const fetchById = async (event: H3Event) => {
try {
const { apiUrl } = useRuntimeConfig().public
const { pollId}: any = getQuery(event)
const { item }: any = await $fetch(`${apiUrl}/cms/poll/${pollId}`, {
method: 'GET',
headers: {
site: 1
}
})
return item
} catch (error) {
handleError(error);
}
}
+20
View File
@@ -0,0 +1,20 @@
export const usePollOptionStore = defineStore('usepollOptionStore', () => {
async function fetchByPollId(id: string) {
const { data, error } = await useFetch<any>(`/api/services/poll-option/pollId`, {
query: {
pollId: id
}
})
if(error.value) {
return null
}
return data.value
}
return { fetchByPollId }
})
if(import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useTagStore, import.meta.hot))
}
+19
View File
@@ -0,0 +1,19 @@
export const usePollResponseStore = defineStore('usepollResponseStore', () => {
async function create(pollResponse: any) {
const { data, error } = await useFetch<any>(`/api/services/poll-response`, {
method: 'POST',
body: pollResponse
})
if(error.value) {
return null
}
return data.value
}
return { create }
})
if(import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useTagStore, import.meta.hot))
}
+20
View File
@@ -0,0 +1,20 @@
export const usePollStore = defineStore('usepollStore', () => {
async function fetchById(id: string) {
const { data, error } = await useFetch<any>(`/api/services/poll-by-id`, {
query: {
pollId: id
}
})
if(error.value) {
return null
}
return data.value
}
return { fetchById }
})
if(import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useTagStore, import.meta.hot))
}