feat: new layout

This commit is contained in:
MoreStrive
2024-06-28 15:39:26 +07:00
parent ab3419bd5f
commit ad962eda86
134 changed files with 4977 additions and 2985 deletions
@@ -0,0 +1,178 @@
<script setup lang="ts">
const type = ref("");
import { enumPageComponentTemplates } from "@/definitions/enum";
import { DEFAULT_QUERY_DROP } from "@/utils/parseSQL";
import { getInputValue } from "@/utils/parseSQL";
const props = defineProps<{
dataResult?: any;
dataType?: any;
dataQuery?: any;
layout?: string;
label?: string;
}>();
const LAYOUT_PARSE = computed(() => {
const designObject = props.label ? getInputValue(props.label, "OBJECT") : {};
return Object.assign({}, designObject);
});
const emit = defineEmits(["selectComponent", "dropData"]);
const selectComponent = () => {
emit("selectComponent");
};
const parseData = computed(() => {
if (!props.dataResult) return;
const result = getInputValue(props.dataResult, "OBJECT");
switch (result?.contentType) {
case 1:
type.value = "";
// type.value = "Image";
break;
case 2:
type.value = "Image";
break;
case 3:
type.value = "Postcard";
break;
case 4:
type.value = "Video";
break;
case 5:
if (result?.layoutType === 4) {
type.value = "Emagazine";
break;
}
if (result?.layoutType === 3) {
type.value = "Infographics";
break;
}
}
return result;
});
const drop = (e: any) => {
if (e.dataTransfer.getData(`${enumPageComponentTemplates.ARTICLE}`)) {
const data = e.dataTransfer.getData(`${enumPageComponentTemplates.ARTICLE}`);
const { dataType, dataResult } = JSON.parse(data);
const dataQuery = DEFAULT_QUERY_DROP(dataType, dataResult.id);
emit("dropData", {
dataType,
dataResult,
dataQuery: dataQuery,
});
}
};
</script>
<template>
<article class="card-audio" :class="LAYOUT_PARSE['article_Class']" @click="selectComponent" @dragover.prevent @drop.stop.prevent="drop" :style="LAYOUT_PARSE['article']">
<img :src="parseData?.thumbnail ? parseData?.thumbnail : 'https://indiaeducationdiary.in/wp-content/uploads/2021/02/SD-default-image.png'" :alt="parseData?.title?.replace(/<[^>]+>/g, '')" />
<div class="card-audio__content">
<span>
<template v-if="type === 'Image'">
<svg width="28" height="22" viewBox="0 0 28 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M25.25 0.5H7.25C5.98438 0.5 5 1.53125 5 2.75V14.75C5 16.0156 5.98438 17 7.25 17H25.25C26.4688 17 27.5 16.0156 27.5 14.75V2.75C27.5 1.53125 26.4688 0.5 25.25 0.5ZM10.9531 3.5C11.75 3.5 12.4531 4.20312 12.4531 5C12.4531 5.84375 11.7969 6.5 10.9531 6.5C10.1094 6.5 9.45312 5.84375 9.45312 5C9.45312 4.20312 10.1562 3.5 10.9531 3.5ZM23.6562 13.625C23.5156 13.8594 23.2344 14 23 14H9.5C9.17188 14 8.9375 13.8594 8.79688 13.625C8.70312 13.3438 8.70312 13.0625 8.89062 12.8281L12.1719 8.32812C12.3125 8.14062 12.5 8 12.7812 8C13.0156 8 13.2031 8.14062 13.3438 8.32812L14.4219 9.78125L17.375 5.375C17.4688 5.14062 17.7031 5 17.9844 5C18.2188 5 18.4531 5.14062 18.5938 5.375L23.6094 12.875C23.75 13.0625 23.75 13.3906 23.6562 13.625ZM21.875 19.25H6.125C4.25 19.25 2.75 17.75 2.75 15.875V4.625C2.75 4.01562 2.23438 3.5 1.625 3.5C0.96875 3.5 0.5 4.01562 0.5 4.625V15.875C0.5 19.0156 2.98438 21.5 6.125 21.5H21.875C22.4844 21.5 23 21.0312 23 20.375C23 19.7656 22.4844 19.25 21.875 19.25Z"
fill="white"
/>
</svg>
</template>
</span>
<div class="card-audio__type-category">
<div class="card-audio__type" v-if="type">{{ type }}</div>
<nuxt-link v-if="parseData" class="card-audio__category" :style="LAYOUT_PARSE['category-article']" :class="LAYOUT_PARSE['category-article_Class']">{{
parseData?.category?.title
}}</nuxt-link>
<span v-else class="empty-block" style="height: 8px; width: 30px"></span>
</div>
<nuxt-link>
<h2 v-html="parseData.title" v-if="parseData" :class="LAYOUT_PARSE['title_Class']" :style="LAYOUT_PARSE['h3.title']"></h2>
<span v-else class="empty-block" style="height: 8px"></span>
</nuxt-link>
</div>
<div v-html="LAYOUT_PARSE.styleClasses"></div>
</article>
</template>
<style lang="scss">
.card-audio {
position: relative;
width: 100%;
padding-bottom: 56.25%; //tỷ lệ 9 /16;;
img {
position: absolute;
height: 100%;
width: 100%;
object-fit: cover;
z-index: 1;
}
.card-audio__content {
position: absolute;
width: 100%;
bottom: 0;
padding: 20px 30px;
z-index: 3;
text-align: center;
h2 {
color: #fff;
margin: 12px 0 20px 0;
font-size: 24px;
font-weight: 700;
line-height: 130%;
&:hover {
color: #9e1e0f;
}
}
}
.card-audio__type-category {
display: flex;
gap: 2px;
justify-content: center;
margin-top: 12px;
.card-audio__type,
.card-audio__category {
padding: 0 10px;
font-size: 12px;
line-height: 180%;
text-transform: uppercase;
}
.card-audio__type {
background-color: #ed1c24;
color: #fff;
}
.card-audio__category {
background-color: #fff;
color: #000;
}
}
&::after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.25);
z-index: 2;
}
.empty-block {
background-color: #409eff;
height: 100px;
display: block;
}
}
</style>
@@ -0,0 +1,286 @@
<script lang="ts" setup>
import { enumPageComponentTemplates } from "@/definitions/enum";
import { DEFAULT_QUERY_DROP } from "@/utils/parseSQL";
import { getInputValue } from "@/utils/parseSQL";
import { formatDate } from "@/utils/filters";
const props = defineProps<{
dataResult?: any;
dataType?: any;
dataQuery?: any;
layout?: string;
label?: string;
}>();
const LAYOUT_PARSE = computed(() => {
const designObject = props.label ? getInputValue(props.label, "OBJECT") : {};
return Object.assign({}, designObject);
});
const emit = defineEmits(["selectComponent", "dropData"]);
const selectComponent = () => {
emit("selectComponent");
};
const parseData = computed(() => {
if (!props.dataResult) return;
const result = getInputValue(props.dataResult, "OBJECT");
return result;
});
const drop = (e: any) => {
if (e.dataTransfer.getData(`${enumPageComponentTemplates.ARTICLE}`)) {
const data = e.dataTransfer.getData(`${enumPageComponentTemplates.ARTICLE}`);
const { dataType, dataResult } = JSON.parse(data);
const dataQuery = DEFAULT_QUERY_DROP(dataType, dataResult.id);
emit("dropData", {
dataType,
dataResult,
dataQuery: dataQuery,
});
}
};
</script>
<template>
<!-- <article class="article-card-default">
<div class="article-card-default__content">
<template v-if="currentArticle.tags && currentArticle?.tags.length > 0">
<nuxt-link class="article-card-default__tag" :to="`/${currentArticle?.tags[0].code}`">
<h5>{{ currentArticle?.tags[0].title }}</h5>
</nuxt-link>
</template>
<nuxt-link class="article-card-default__title" :to="`/bai-viet/${currentArticle.code}`">
<h2 v-html="currentArticle.title"></h2>
<p v-html="currentArticle.intro"></p>
</nuxt-link>
<div class="article-card-default__bottom">
<span>{{ formatDate(String(currentArticle.createdOn), "DD/MM/YYYY | HH:mm") }}</span> /
<nuxt-link :to="`/${currentArticle.category?.code}`"> {{ currentArticle.category?.title }}</nuxt-link>
</div>
</div>
<div class="article-card-default__thumbnail">
<figure>
<nuxt-link :to="`bai-viet${currentArticle.code}`">
<img :src="currentArticle?.thumbnail ? currentArticle?.thumbnail : 'http://picsum.photos/1024/600?random=1'" :alt="currentArticle?.title" />
</nuxt-link>
</figure>
</div>
</article> -->
<article
class="basic-article border-custom"
:class="LAYOUT_PARSE['article_Class']"
@click="selectComponent"
@dragover.prevent
@drop.stop.prevent="drop"
:style="LAYOUT_PARSE['article']"
>
<div class="basic-article_thumbnail" :class="LAYOUT_PARSE['thumbnail_Class']" :style="LAYOUT_PARSE['div.basic-article_thumbnail']">
<template v-if="parseData">
<img class="object-fit-cover" :src="parseData.thumbnail ? parseData.thumbnail : '/images/default-thumbnail.jpg'" :alt="parseData.title?.replace(/<[^>]+>/g, '')" />
</template>
<span v-else class="empty-block" style="width: 100%; height: 100%; min-height: 50px"></span>
</div>
<div class="basic-article_content" :class="[!parseData && 'no-data']">
<template v-if="parseData?.topics && parseData?.topics.length > 0">
<nuxt-link class="article-card-default__topic" :to="`/${parseData?.topics[0].code}`" :style="LAYOUT_PARSE['topic']">
<h5>{{ parseData?.topics[0].title }}</h5>
</nuxt-link>
</template>
<h3 class="line-clamp" :class="LAYOUT_PARSE['title_Class']" :style="LAYOUT_PARSE['h3.title']">
<template v-if="parseData">
{{ parseData.title?.replace(/<[^>]+>/g, "") }}
</template>
<span v-else class="empty-block" style="height: 8px"></span>
</h3>
<div class="article-card-default__bottom" v-if="LAYOUT_PARSE.layout === 'row'">
<span :style="LAYOUT_PARSE['time']" style="margin-right: 5px" :class="LAYOUT_PARSE['time_Class']">{{
formatDate(String(parseData?.createdOn), "DD/MM/YYYY | HH:mm")
}}</span>
<nuxt-link :style="LAYOUT_PARSE['category-article']" :class="LAYOUT_PARSE['category-article_Class']">{{ parseData?.category?.title }}</nuxt-link>
</div>
<p class="mb-0 line-clamp" :class="LAYOUT_PARSE['paragraph_Class']" :style="LAYOUT_PARSE['p.paragraph']">
<template v-if="parseData">
{{ parseData.intro?.replace(/<[^>]+>/g, "") }}
</template>
<span v-else class="empty-block" style="height: 5px"></span>
</p>
<div class="article-card-default__bottom" v-if="LAYOUT_PARSE?.layout !== 'row'" :style="LAYOUT_PARSE['metadata']">
<span :style="LAYOUT_PARSE['time']" style="margin-right: 5px" :class="LAYOUT_PARSE['time_Class']">{{
formatDate(String(parseData?.createdOn), "DD/MM/YYYY | HH:mm")
}}</span>
<nuxt-link :style="LAYOUT_PARSE['category-article']" :class="LAYOUT_PARSE['category-article_Class']">{{ parseData?.category?.title }}</nuxt-link>
</div>
</div>
<div v-html="LAYOUT_PARSE.styleClasses"></div>
</article>
</template>
<style lang="scss" scoped>
.article-card-default {
display: flex;
gap: 20px;
&__content {
flex: 1;
.article-card-default__title {
color: #000;
h2 {
display: inline-block;
/* margin: 12px 0 20px 0; */
font-size: 24px;
font-weight: 700;
line-height: 130%;
&:hover {
color: #9e1e0f;
}
}
p {
font-size: 14px;
line-height: 150%;
font-weight: 400;
}
}
}
&__thumbnail {
width: 60%;
}
}
.flex-column {
flex-direction: column;
}
.article-card-default__topic {
position: relative;
/* margin: 0 0 12px 12px; */
// background-color: #151411;
display: inline-block;
h5 {
font-size: 12px;
text-transform: uppercase;
// color: #fff;
padding: 0 12px;
height: 100%;
margin: 0;
border: 1px solid #000;
line-height: 180%;
font-weight: 300;
}
&::after {
position: absolute;
content: "";
display: block;
width: 12px;
height: 100%;
background-color: #ed1c24;
left: -12px;
top: 0;
}
}
.article-card-default__bottom {
font-size: 12px;
color: rgba(0, 0, 0, 0.35);
/* margin-top: 10px; */
a {
color: #ed1c24;
}
}
figure {
margin: 0;
padding: 0;
width: 100%;
}
img {
width: 100%;
object-fit: cover;
}
h3,
p {
margin: 0;
}
.basic-article {
display: flex;
gap: 16px;
height: 100%;
padding: 20px;
background-size: cover;
flex-direction: column;
&.no-data {
gap: 5px !important;
padding: 0;
}
.line-clamp {
display: -webkit-box;
/* -webkit-line-clamp: 3; */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.basic-article_thumbnail {
width: 100%;
}
&.border-custom {
border-color: #e5e5e5 !important;
}
/* &.horizontal {
flex-direction: row;
.basic-article_thumbnail {
width: 40%;
}
&.reverse {
flex-direction: row-reverse;
}
} */
&_thumbnail {
img {
width: 100%;
border-radius: 2px;
aspect-ratio: 16/10;
}
}
&_content {
/* padding: 10px 0px; */
display: flex;
flex-direction: column;
gap: 10px;
flex: 1;
&.no-data {
padding: 0px;
}
h3 {
font-size: 16px;
}
p {
font-size: 14px;
/* margin-top: 10px; */
opacity: 85%;
}
}
.empty-block {
background-color: #409eff;
height: 100px;
display: block;
}
}
</style>
@@ -0,0 +1,2 @@
export { default as Article_Card_Default } from './Card.vue'
export { default as Article_Card_Audio } from './Audio.vue'
@@ -0,0 +1,39 @@
<script lang="ts" setup>
import { enumPageComponentTemplate, enumPageComponentKey, enumPageComponentLayouts } from "@/definitions/enum";
import { Article_Card_Default, Article_Card_Audio } from "./index";
const _props = defineProps<{
settings: any;
component?: any;
content?: any;
}>();
const definedDynamicComponent: Record<string, any> = {
[enumPageComponentLayouts[enumPageComponentTemplate[enumPageComponentKey.ARTICLE]["ARTICLE_CARD"]]["CARD_DEFAULT"]]: Article_Card_Default,
[enumPageComponentLayouts[enumPageComponentTemplate[enumPageComponentKey.ARTICLE]["ARTICLE_CARD"]]["CARD_AUDIO"]]: Article_Card_Audio,
};
const getCurrentComponent = computed(() => {
console.log(_props.settings.layout, enumPageComponentLayouts[enumPageComponentTemplate[enumPageComponentKey.ARTICLE]["ARTICLE_CARD"]]["CARD_DEFAULT"]);
return _props.settings.layout;
});
const GET_PROPS = computed(() => {
return () => {
let props: any = {};
if (_props.settings) {
for (const [key, value] of Object.entries(_props.settings)) {
props = {
...props,
[key]: value,
};
}
return props;
}
};
});
</script>
<template>
<component :is="definedDynamicComponent[getCurrentComponent]" v-bind="{ ...GET_PROPS(), component: _props.component, settings: _props.settings }" />
</template>