This commit is contained in:
MoreStrive
2024-05-30 18:06:50 +07:00
parent 8ca31c6b18
commit 2aa5607c48
147 changed files with 5815 additions and 1 deletions
@@ -0,0 +1,86 @@
<script setup lang="ts">
import DynamicComponent from "~/components/dynamic-page/page-component/templates/index.vue";
import { COLLECTION_QUERY_DROP, getValueStringWithKeyAndColon, getInputValue } from '@/utils/parseSQL';
import { isEmpty } from "lodash";
const _props = defineProps<{
dataResult?: any[];
dataQuery?: string;
layout?: string;
}>();
const SETTING_OPTIONS = {
MAX_ELEMENT: 5,
TEMPLATE: "Article",
LAYOUT: "LAYOUT:vertical"
};
const LAYOUT_PARSE = computed(() => {
const parseLayout = _props.layout?.split("-")?.map((_layout: any) => {
const parseItem = _layout.split(":");
return {
[parseItem[0]]: parseItem[1],
};
});
return Object.assign({}, ...parseLayout);
});
const _dataResult = computed(() => {
let _components = Array(Number(LAYOUT_PARSE.value.MAX) || SETTING_OPTIONS.MAX_ELEMENT).fill(null);
const result = getInputValue(_props.dataResult, 'ARRAY');
result && result.length > 0 && _components.map((_ : any, index : any) => {
_components[index] = result[index] || null;
})
return _components;
});
</script>
<template>
<div>
<div class="collection-container p-2" :class="LAYOUT_PARSE['LAYOUT'] || 'horizontal'">
<div v-for="(component, index) in _dataResult" :key="index">
<template v-if="!isEmpty(component)">
<DynamicComponent
:settings="{
template: LAYOUT_PARSE.TYPE || SETTING_OPTIONS.TEMPLATE,
layout: `LAYOUT:${LAYOUT_PARSE.DATA.toLowerCase()}` || SETTING_OPTIONS.LAYOUT,
dataResult: { ...component },
}"
@drop-data="dropData"
/>
</template>
<template v-else>
<DynamicComponent
:settings="{
template: LAYOUT_PARSE.TYPE || SETTING_OPTIONS.TEMPLATE,
layout: `LAYOUT:${LAYOUT_PARSE.DATA.toLowerCase()}` || SETTING_OPTIONS.LAYOUT,
}"
@drop-data="dropData"
/>
</template>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.collection-container {
display: grid;
gap: 10px;
&.vertical {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
&.horizontal {
grid-template-rows: auto;
grid-auto-flow: column;
}
.empty {
min-height: 100px;
border-radius: 6px;
background: #409eff;
}
&.noData {
border-radius: 6px;
}
}
</style>
@@ -0,0 +1,136 @@
<script lang="ts" setup>
import { enumPageComponentTemplates } from "@/definitions/enum";
import { DEFAULT_QUERY_DROP, getInputValue } from '@/utils/parseSQL';
const props = defineProps<{
dataResult?: any
dataType?: any
dataQuery?: any
layout?: string
}>()
const LAYOUT_PARSE = computed(() => {
const parseLayout = props.layout?.split('-')?.map((_layout : any) => {
const parseItem = _layout.split(':')
return {
[parseItem[0]]: parseItem[0] === 'HIDE' ? parseItem[1].split(',') : parseItem[1],
};
}) || [];
return Object.assign({}, ...parseLayout);
})
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="basic-article" :class="[LAYOUT_PARSE['LAYOUT'] || 'horizontal', !parseData && 'no-data', LAYOUT_PARSE['REVERSE'] ? 'reverse' : '']" @click="selectComponent" @dragover.prevent @drop.stop.prevent="drop">
<div v-if="!LAYOUT_PARSE['HIDE'] || !LAYOUT_PARSE['HIDE'].includes('thumbnail')" class="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']">
<div>
<h3 v-if="!LAYOUT_PARSE['HIDE'] || !LAYOUT_PARSE['HIDE'].includes('title')" class="mb-1 text-truncate-two-lines">
<template v-if="parseData">
{{ parseData.title?.replace(/<[^>]+>/g, '') }}
</template>
<span v-else class="empty-block" style="height: 8px;"></span>
</h3>
<p v-if="!LAYOUT_PARSE['HIDE'] || !LAYOUT_PARSE['HIDE'].includes('paragraph')" class="mb-0 text-truncate-two-lines">
<template v-if="parseData">
{{ parseData.intro?.replace(/<[^>]+>/g, '') }}
</template>
<span v-else class="empty-block" style="height: 5px;"></span>
</p>
</div>
</div>
</article>
</template>
<style lang="scss" scoped>
.basic-article {
display: grid;
gap: 10px;
height: 100%;
&.no-data {
gap: 5px !important;
}
&.vertical {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
&.horizontal {
grid-template-columns: 1fr 1fr;
&.reverse {
.basic-article_thumbnail {
grid-column: 2;
}
.basic-article_content {
grid-row: 1;
}
}
}
&_thumbnail {
flex: 1;
img {
width: 100%;
border-radius: 6px;
aspect-ratio: 16/10;
}
}
&_content {
padding: 10px 0px;
&.no-data {
padding: 0px;
}
h3 {
font-size: 15px;
}
p {
font-size: 12px;
margin-top: 10px;
}
}
.empty-block {
background-color: #409eff;
height: 100px;
display: block;
}
}
</style>
@@ -0,0 +1,95 @@
<script setup lang="ts">
import { isEmpty } from "lodash";
import { COLLECTION_QUERY_DROP, getValueStringWithKeyAndColon, getInputValue } from '@/utils/parseSQL';
const emit = defineEmits(["dropData", "selectComponent"]);
const _props = defineProps<{
dataResult?: any[];
dataQuery?: string;
}>();
const SETTING_OPTIONS = {
MAX_ELEMENT: 5,
};
const _dataResult = computed(() => {
let _components = Array(SETTING_OPTIONS.MAX_ELEMENT).fill(null);
const result = getInputValue(_props.dataResult, 'ARRAY');
result && result.length > 0 && _components.map((_ : any, index : any) => {
_components[index] = result[index] || null;
})
return _components;
});
async function dropData(event: any) {
const { dataResult, dataType } = JSON.parse(event.dataTransfer.getData("category"));
const checkDataResult = getInputValue(_props.dataResult, 'ARRAY');
const result = _props.dataResult ? [...checkDataResult, { ...dataResult }] : [{...dataResult}];
const getDataQuery = _props.dataQuery ?
COLLECTION_QUERY_DROP(dataType, getValueStringWithKeyAndColon(_props.dataQuery) + "," + dataResult.id)
: COLLECTION_QUERY_DROP(dataType, dataResult.id);
emit("dropData", {
dataResult: result,
dataType,
dataQuery: getDataQuery,
});
}
const selectComponent = () => {
emit("selectComponent");
};
</script>
<template>
<div>
<div class="categories-container p-2" @click="selectComponent">
<div
v-for="(component, index) in _dataResult"
:key="index"
:class="isEmpty(component) ? 'empty' : 'category'"
>
<template v-if="!isEmpty(component)">
<h3>{{ component.title }}</h3>
</template>
<div
v-else
@dragover.prevent
@drop.stop.prevent="dropData($event)"
>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.categories-container {
display: flex;
gap: 10px;
align-items: flex-end;
.category {
height: 100%;
h3 {
font-weight: 500;
font-size: 14px;
margin: 0px !important;
}
&:first-child {
h3 {
font-weight: 600;
font-size: 17px;
}
}
}
.empty {
border-radius: 6px;
background: #409eff;
width: 50px;
> div {
min-height: 20px;
}
}
}
</style>
@@ -0,0 +1,156 @@
<script setup lang="ts">
const props = defineProps<{}>();
</script>
<template>
<div class="player">
<div class="player__track">
<input class="player__track-range" type="range" disabled />
<div class="player__time">
<span class="player__time-current">00:00</span>
<span class="player__time-duration">00:00</span>
</div>
</div>
<div class="player__controls">
<div class="player__speed">
<button class="player__speed-button">
<span class="player__speed-label">Tốc độ phát</span>
<span class="player__speed-value">1.0x</span>
</button>
</div>
<div class="player__actions">
<button class="player__actions-button player__actions-button--replay">
<Icon name="ri:replay-5-fill" class="player__icon player__icon--replay" />
</button>
<button class="player__actions-button player__actions-button--pause">
<Icon name="ri:play-circle-fill" class="player__icon player__icon--pause" />
</button>
<button class="player__actions-button player__actions-button--forward">
<Icon name="ri:forward-5-line" class="player__icon player__icon--forward" />
</button>
</div>
<div class="player__volume">
<button class="player__volume-button">
<div class="player__volume-control">
<Icon name="ri:volume-up-fill" class="player__icon player__icon--volume" />
<input class="player__volume-range" type="range" disabled />
</div>
</button>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.player {
&__track {
width: 100%;
}
&__track-range {
width: 100%;
height: 5px;
accent-color: #fff;
cursor: pointer;
}
&__time {
display: flex;
justify-content: space-between;
padding: 0 1rem;
&-current,
&-duration {
font-size: 10px;
font-family: 'Raleway', sans-serif;
font-weight: normal;
color: #fff;
}
}
&__controls {
width: 100%;
padding: 0 1rem;
display: flex;
justify-content: center;
align-items: center;
& > div {
display: flex;
justify-content: space-between;
align-items: center;
}
}
&__speed {
&-button {
color: #fff;
background-color: transparent;
font-size: 0.75rem;
display: flex;
gap: 0.25rem;
&-value {
font-weight: bold;
font-size: 0.75rem;
}
}
}
&__actions {
&-button {
background-color: transparent;
padding: 0.5rem;
border-radius: 100%;
color: white;
width: fit-content;
height: fit-content;
&--replay:hover,
&--forward:hover {
background-color: #d6d3d1;
}
}
}
&__icon {
&--replay,
&--forward,
&--pause {
font-size: 20px;
}
&--pause {
font-size: 44px;
}
}
&__volume {
display: flex;
align-items: center;
&-button {
background-color: transparent;
color: white;
}
&-control {
display: flex;
align-items: center;
gap: 0.5rem;
& .player__icon--volume {
font-size: 1.125rem;
}
& .player__volume-range {
accent-color: #fff;
width: 3rem;
height: 5px;
cursor: pointer;
}
}
}
}
button{
border: 0;
}
</style>
@@ -0,0 +1,191 @@
<script setup lang="ts">
import AudioPlayer from './AudioPlayer.vue'
</script>
<template>
<div class="banner">
<div class="banner__background" style="background-image: url('https://acp-api.vpress.vn/Resources/%E1%BA%A2nh/0bf02739-de1e-4899-9a2e-287c5d949250.jpg')">
<div class="banner__overlay"></div>
<Wrap class="banner__content">
<div class="banner__inner">
<div class="article">
<div class="article__image-container">
<div class="article__image-wrapper" style="background-image: url('https://acp-api.vpress.vn/Resources/%E1%BA%A2nh/0bf02739-de1e-4899-9a2e-287c5d949250.jpg')">
<img src="https://acp-api.vpress.vn/Resources/%E1%BA%A2nh/0bf02739-de1e-4899-9a2e-287c5d949250.jpg" alt="" class="article__image" />
</div>
</div>
<div class="article__content">
<div class="article__header">
<div class="article__header-text">
<h1 class="article__title">Podcast Truyện ngắn: Như cơi đựng trầu</h1>
<time class="article__date">T2, 29 Th01 2024 16:57</time>
</div>
</div>
<div class="article__intro">
<div class="article__intro-text">Tình cảm vợ chồng êm ấm 12 năm, tối nay được định đoạt bằng tờ giấy hồn, vốn người dễ xúc động nên trong lúc viết, Ngân Thương để mấy giọt nước mắt rơi xuống làm đôi chỗ bị nhòe đi.</div>
</div>
<div class="article__audio">
<AudioPlayer />
</div>
</div>
</div>
</div>
</Wrap>
</div>
</div>
</template>
<style scoped lang="scss">
.banner {
&__background {
width: 100%;
height: 60px;
background-size: cover;
@media (min-width: 768px) {
height: 25rem;
}
position: relative;
background-position: center;
}
&__overlay {
position: absolute;
inset: 0;
background-color: black;
opacity: 0.8;
z-index: 1;
}
&__content {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
&__inner {
width: 100%;
height: 40px;
@media (min-width: 768px) {
height: 80px;
}
position: absolute;
inset: 0;
z-index: 2;
.article {
display: grid;
grid-template-columns: repeat(10, 1fr);
width: 100%;
&__image-container {
grid-column: span 3;
display: flex;
justify-content: center;
align-items: center;
height: 15rem;
min-width: 100px;
@media (min-width: 768px) {
height: 20rem;
margin: 0 2rem;
}
margin: 0 0.5rem;
}
&__image-wrapper {
height: 10rem;
@media (min-width: 768px) {
height: 15rem;
}
width: 100%;
border-radius: 1.5rem 0 0 1.5rem;
padding: 0.5rem;
overflow: hidden;
position: relative;
background-size: cover;
z-index: 1;
&::after {
content: "";
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: #000;
opacity: 0.3;
position: absolute;
z-index: 2;
}
}
&__image {
position: relative;
z-index: 3;
height: 10rem;
@media (min-width: 768px) {
height: 15rem;
}
width: 100%;
object-fit: contain;
}
&__content {
grid-column: span 7;
display: grid;
grid-template-columns: repeat(12, 1fr);
position: relative;
}
&__header {
grid-column: span 12;
display: grid;
grid-template-columns: repeat(12, 1fr);
margin-top: 2rem;
&-text {
grid-column: span 11;
}
}
&__subtitle {
font-size: 14px;
font-weight: bold;
color: rgba(255, 255, 255, 0.6);
}
&__title {
font-size: 19px;
color: #fff;
font-weight: bold;
font-family: "SFD";
}
&__date {
margin-top: 0.125rem;
font-size: 14px;
color: #fff;
}
&__intro {
grid-column: span 12;
margin-bottom: 1rem;
display: none;
@media (min-width: 768px) {
display: block;
}
}
&__intro-text {
text-align: left;
font-size: 16px;
color: #fff;
font-family: "SFD";
}
&__audio {
grid-column: span 10;
}
}
}
}
</style>
@@ -0,0 +1,74 @@
<template>
<article class="article">
<div id="article-detail" class="article__detail">
<div>
<video controls="controls" width="100%" height="auto" data-file-id="149" data-resource="https://acp-api.vpress.vn/Resources/Video/983d2f57-7743-472f-b22d-fc73085af6d5.mp4" data-title="Download.mp4">
<source src="https://acp-api.vpress.vn/Resources/Video/983d2f57-7743-472f-b22d-fc73085af6d5.mp4" type="video/mp4" />
</video>
</div>
</div>
<div class="article__sidebar">
<div class="article__sidebar-content">
<h1 class="article__title">Tranh cãi chuyện 'quán không nhận chuyển khoản'</h1>
<div class="article__author-info">
<div class="article__author">
<p class="article__author-name">Thanh Huệ</p>
</div>
<span class="article__separator">-</span>
<p class="article__date">T4, 15 Th05 2024 10:55</p>
</div>
<div id="article-brief" class="article__brief">
<div class="article__intro-text">Những ngày cận Tết tại Nội, các hội thi hoa đào, quất cảnh với đa dạng các sản phẩm độc đáo, bắt mắt đ đuợc các nghệ nhân đem đến cho khách tham quan chiêm ngưỡng.</div>
</div>
</div>
</div>
</article>
</template>
<style scoped lang="scss">
.article {
width: 100%;
display: flex;
/* flex-direction: column; */
gap: 1rem; // Equivalent to gap-4
margin-top: 1rem; // Equivalent to mt-4
background-color: #f7f7f7;
&__detail {
flex: 1;
iframe,
video {
width: 100%;
max-width: 100%;
&.iframe {
max-height: 13rem; // Equivalent to max-h-52
}
}
}
&__sidebar {
width: 50%;
&-content {
width: 100%;
}
}
&__title {
font-size: 17px; // Equivalent to text-2xl
font-weight: 600;
text-align: left;
}
&__author-info {
display: flex;
align-items: center;
gap: 2px;
}
p {
margin: 0;
}
video {
pointer-events: none;
}
}
</style>
@@ -0,0 +1,7 @@
// Article
export { default as Article_BasicCard } from './articles/individuals/Card.vue'
export { default as Article_BasicCollection } from './articles/collections/BasicCollection.vue'
// Category
export { default as BasicCategories } from './categories/BasicCategories.vue'
export { default as CollectionPaging } from './pageCategories/collection_page.vue'
@@ -0,0 +1,37 @@
<script lang="ts" setup>
import { enumPageComponentTemplates } from "@/definitions/enum";
import { Article_BasicCard, BasicCategories, Article_BasicCollection } from "./index";
const _props = defineProps<{
settings: any;
component?: any;
}>();
const definedDynamicComponent: Record<string, any> = {
[enumPageComponentTemplates.ARTICLE]: Article_BasicCard,
[enumPageComponentTemplates.CATEGORY]: BasicCategories,
[enumPageComponentTemplates.COLLECTION]: Article_BasicCollection,
};
const getCurrentComponent = computed(() => `${_props.settings.template}`);
const GET_PROPS = computed(() => {
return () => {
let props: any = {};
if (_props.settings) {
for (const [key, value] of _props.settings ? 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 }" /> -->
<component :is="definedDynamicComponent[getCurrentComponent]" v-bind="{ ...(GET_PROPS()), component: _props.component }" />
</template>
@@ -0,0 +1,194 @@
<script setup lang="ts">
import { isEmpty } from "lodash";
import DynamicComponent from "~/components/dynamic-page/page-component/templates/index.vue";
import { COLLECTION_PAGING_QUERY_DROP } from '@/utils/parseSQL';
const router = useRouter();
const route = useRoute();
const emit = defineEmits(["dropData", "selectComponent"]);
const _props = defineProps<{
dataResult?: any[];
dataQuery?: string;
component?: any;
}>();
const SETTING_OPTIONS = {
MAX_ELEMENT: 5,
TEMPLATE: "Article",
LAYOUT: "LAYOUT:horizontal",
};
// const page = ref(1);
const limit = ref(1);
const totals = ref(2);
const category = ref(0);
const listArticleByCategory = ref([]);
const type = "Article";
// watch(
// () => _props.dataResult,
// (newValue) => {
// const result = getInputValue(newValue, "ARRAY");
// listArticleByCategory.value = result;
// }
// );
const dropData = (event: any) => {
const queryBy = {
Category: "Categories",
};
const { dataResult, dataType } = JSON.parse(event.dataTransfer.getData("category"));
// const getDataQuery = `Get[${type}] Top[20] With[${queryBy[dataType]}:${dataResult.id}]`;
const getDataQuery = COLLECTION_PAGING_QUERY_DROP(type, { key: queryBy[dataType], value: dataResult.id })
category.value = dataResult.id;
emit("dropData", {
dataResult: [],
dataType,
dataQuery: getDataQuery,
});
};
//?cpn_1=page:2&cpn_2=page:1
// Get[Article] Top[5] With[Categories:1]
const select = (page: number) => {
const componentId = _props.component?.id;
if (componentId) {
router.push({
query: {
...route.query,
[`cpn_${componentId}`]: `page:${page}`,
},
});
}
};
const handleRouteChange = (query: any) => {
const [_, value] = query[`cpn_${_props.component?.id}`]?.split(":");
if (value) {
loadPage(Number(value));
}
};
onBeforeMount(()=>{
const result = getInputValue( _props.dataResult, "ARRAY");
listArticleByCategory.value = result;
handleRouteChange(route.query)
})
const loadPage = (page: string | number) => {
console.log(`Loading page ${page}`);
// listArticleByCategory.value =
};
watch(
() => route.query,
(newQuery) => {
handleRouteChange(newQuery);
}
);
</script>
<template>
<section>
<div class="section-container" @dragover.prevent @drop.stop.prevent="dropData($event)"
:class="[listArticleByCategory && listArticleByCategory?.length > 0 ? '' : 'noData']">
<div class="collection-container">
<template v-if="category">
<template v-if="listArticleByCategory?.length > 0">
<template v-for="(component, index) in listArticleByCategory" :key="index">
<DynamicComponent
v-if="!isEmpty(component)"
:settings="{
template: SETTING_OPTIONS.TEMPLATE,
layout: SETTING_OPTIONS.LAYOUT,
dataResult: { ...component },
}"
/>
</template>
</template>
<template v-else>
<el-result icon="success" title="Success" sub-title="Nội dung danh sách bài viết sẽ đây"> </el-result>
</template>
</template>
<template v-else><el-empty image-size="90px" description="Kéo Category vào đây" /></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>
</a>
<a class="btn-page" @click="() => select(index + 1)" v-for="(_, index) in totals">{{ 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>
</a>
</div>
</div>
</div>
</section>
</template>
<style lang="scss" scoped>
.section-container {
.empty {
min-height: 100px;
border-radius: 6px;
background: #409eff;
}
.collection-container {
display: grid;
gap: 10px;
}
.basic-article {
&.article {
margin-bottom: 10px;
display: flex;
pointer-events: none;
}
}
&.noData {
border-radius: 6px;
}
.flex {
display: flex;
margin-top: 10px;
justify-content: center;
overflow-x: auto;
}
.button-page {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.btn-page {
width: 40px;
height: 40px;
text-align: center;
line-height: 36px;
border: 1px solid #409eff;
border-radius: 3px;
margin-left: 10px;
display: flex;
justify-content: center;
align-items: center;
}
}
.el-empty {
padding: 12px 0;
}
</style>
@@ -0,0 +1,70 @@
<script setup lang="ts">
import DynamicComponent from "~/components/dynamic-page/page-component/templates/index.vue";
import DynamicSection from "~/components/dynamic-page/page-section/templates/index.vue";
const props = defineProps<{
type: string;
id: any;
}>();
import { useDynamicPageStore } from '~/stores/dynamic-page';
const store = reactive({
dynamicPage: useDynamicPageStore(),
});
const { currentPage } = storeToRefs(useDynamicPageStore());
const defineTypeRecusive = {
COMPONENT: "component",
SECTION: "section",
};
const findDataPosition = computed(() => {
let result = {};
switch (props.type) {
case defineTypeRecusive.COMPONENT:
result = currentPage.value.components && currentPage.value.components.find((component: any) => component.id === props.id);
break;
case defineTypeRecusive.SECTION:
result = currentPage.value.sections && currentPage.value.sections.find((section: any) => section.id === props.id);
break;
default:
result = {};
break;
}
return result;
});
</script>
<template>
<div>
<template v-if="props.type === defineTypeRecusive.COMPONENT">
<DynamicComponent
v-if="findDataPosition && findDataPosition?.id"
:settings="findDataPosition.settings"
:component="findDataPosition"
/>
<div v-else class="empty"></div>
</template>
<template v-else-if="props.type === defineTypeRecusive.SECTION">
<DynamicSection
v-if="findDataPosition && findDataPosition?.id"
:settings="findDataPosition.settings"
:content="findDataPosition.content ? JSON.parse(findDataPosition.content) : null"
:section="findDataPosition"
/>
<div v-else class="empty"></div>
</template>
<template v-else>
<div class="empty"></div>
</template>
</div>
</template>
<style lang="scss" scoped>
.empty {
min-height: 100px;
border-radius: 6px;
background: #409eff;
}
</style>
@@ -0,0 +1,178 @@
<script setup lang="ts">
import RecusiveSection from "~/components/dynamic-page/page-section/RecusiveSection.vue";
import { enumPageSectionLayouts } from "~/definitions/enum";
const props = defineProps<{
layout?: string;
content?: any;
section: any;
}>();
const defineTypeRecusive = {
COMPONENT: "component",
SECTION: "section",
};
const SETTING_OPTIONS = computed(() => {
let _setting_options = {};
switch (props.layout) {
case enumPageSectionLayouts.VERTICAL_TWO:
_setting_options = {
MAX_ELEMENT: 2,
};
break;
case enumPageSectionLayouts.VERTICAL_LEFT_TWO:
_setting_options = {
MAX_ELEMENT: 2,
};
break;
case enumPageSectionLayouts.VERTICAL_RIGHT_TWO:
_setting_options = {
MAX_ELEMENT: 2,
};
break;
case enumPageSectionLayouts.VERTICAL_THREE:
_setting_options = {
MAX_ELEMENT: 3,
};
break;
case enumPageSectionLayouts.VERTICAL_FOUR:
_setting_options = {
MAX_ELEMENT: 4,
};
break;
case enumPageSectionLayouts.HORIZONTAL_ONE:
_setting_options = {
MAX_ELEMENT: 1,
};
break;
case enumPageSectionLayouts.HORIZONTAL_TWO:
_setting_options = {
MAX_ELEMENT: 2,
};
break;
case enumPageSectionLayouts.HORIZONTAL_THREE:
_setting_options = {
MAX_ELEMENT: 3,
};
break;
case enumPageSectionLayouts.HORIZONTAL_FOUR:
_setting_options = {
MAX_ELEMENT: 4,
};
break;
case enumPageSectionLayouts.HORIZONTAL_FIVE:
_setting_options = {
MAX_ELEMENT: 5,
};
break;
case enumPageSectionLayouts.HORIZONTAL_SIX:
_setting_options = {
MAX_ELEMENT: 6,
};
break;
case enumPageSectionLayouts.HORIZONTAL_SEVEN:
_setting_options = {
MAX_ELEMENT: 7,
};
break;
case enumPageSectionLayouts.HORIZONTAL_EIGHT:
_setting_options = {
MAX_ELEMENT: 8,
};
break;
case enumPageSectionLayouts.HORIZONTAL_NINE:
_setting_options = {
MAX_ELEMENT: 9,
};
break;
case enumPageSectionLayouts.HORIZONTAL_TEN:
_setting_options = {
MAX_ELEMENT: 10,
};
break;
default:
_setting_options = {
MAX_ELEMENT: 1,
};
break;
}
return _setting_options;
});
const CLASS_FOR_SECTION = computed(() => {
let _classForSection = {};
switch (props.layout) {
case enumPageSectionLayouts.VERTICAL_TWO:
_classForSection = {
section_layout: "section_layout two_col_layout",
};
break;
case enumPageSectionLayouts.VERTICAL_LEFT_TWO:
_classForSection = {
section_layout: "section_layout three_col_layout",
0: "col-span-2",
};
break;
case enumPageSectionLayouts.VERTICAL_RIGHT_TWO:
_classForSection = {
section_layout: "section_layout three_col_layout",
1: "col-span-2",
};
break;
case enumPageSectionLayouts.VERTICAL_THREE:
_classForSection = {
section_layout: "section_layout three_col_layout",
};
break;
case enumPageSectionLayouts.VERTICAL_FOUR:
_classForSection = {
section_layout: "section_layout four_col_layout",
};
break;
default:
_classForSection = {
section_layout: "section_layout basic_column",
};
break;
}
return _classForSection;
});
</script>
<template>
<div class="section_layout" :class="[CLASS_FOR_SECTION.section_layout]">
<div
v-for="(position, index) in props.content || Array(SETTING_OPTIONS.MAX_ELEMENT).fill({})"
:key="index"
:class="[CLASS_FOR_SECTION[index]]"
>
<RecusiveSection :type="position.type" :id="position.data" :section="props.section" />
</div>
</div>
</template>
<style lang="scss" scoped>
.section_layout {
display: grid;
gap: 10px;
&.basic_column {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
&.two_col_layout {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
&.three_col_layout {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
&.four_col_layout {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.col-span-2 {
grid-column: span 2 / span 2;
}
}
</style>
@@ -0,0 +1 @@
export { default as BASE_LAYOUT } from './Default.vue'
@@ -0,0 +1,61 @@
<script lang="ts" setup>
import { BASE_LAYOUT } from "./index";
import { enumPageSectionLayouts } from "@/definitions/enum";
const _props = defineProps<{
settings?: any;
layout?: string;
content?: any;
section: any;
}>();
const definedDynamicSection: Record<string, any> = {
'Default': BASE_LAYOUT,
[enumPageSectionLayouts.VERTICAL_TWO]: BASE_LAYOUT,
[enumPageSectionLayouts.VERTICAL_LEFT_TWO]: BASE_LAYOUT,
[enumPageSectionLayouts.VERTICAL_RIGHT_TWO]: BASE_LAYOUT,
[enumPageSectionLayouts.VERTICAL_THREE]: BASE_LAYOUT,
[enumPageSectionLayouts.VERTICAL_FOUR]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_ONE]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_TWO]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_THREE]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_FOUR]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_FIVE]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_SIX]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_SEVEN]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_EIGHT]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_NINE]: BASE_LAYOUT,
[enumPageSectionLayouts.HORIZONTAL_TEN]: BASE_LAYOUT,
};
const getCurrentSection = computed(() => _props?.layout || "");
const GET_PROPS = computed(() => {
return () => {
let props: any = {};
if (_props.settings) {
for (const [key, value] of _props.settings ? Object.entries(_props.settings) : []) {
props = {
...props,
[key]: value,
};
}
}
return props;
};
});
</script>
<template>
<component
:is="definedDynamicSection[getCurrentSection] || null"
v-bind="{
...GET_PROPS(),
section: _props.section,
content: _props.content,
}"
>
<slot />
</component>
</template>
@@ -0,0 +1,48 @@
<script setup lang="ts">
import DynamicLayout from '~/components/dynamic-page/page-section/layouts/index.vue';
const emit = defineEmits(['dropComponent', 'dropData', 'selectComponent']);
const props = defineProps<{
label?: any
layout?: string
settings?: any
content?: any
section: any
}>()
</script>
<template>
<section class="section-container">
<h2 class="section__title mb-3" v-if="props.label">{{ props.label || '' }}</h2>
<div class="section_layout">
<template v-if="props.layout">
<DynamicLayout
:layout="props.layout"
:content="props.content"
:settings="props.settings"
:section= "props.section"
/>
</template>
<template v-else>
<div>
Bấm để chọn layout
</div>
</template>
</div>
</section>
</template>
<style lang="scss" scoped>
.section_layout {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 10px;
.empty {
min-height: 100px;
border-radius: 6px;
background: #409eff;
}
}
</style>
@@ -0,0 +1,77 @@
<script setup lang="ts">
import HeaderHomeTemplate from '~/components/dynamic-page/page/templates/components/headers/HeaderHomeTemplate.vue'
import FooterHomeTemplate from '~/components/dynamic-page/page/templates/components/footers/FooterHomeTemplate.vue'
const props = defineProps<{
layout?: any
}>()
const classForLayout = computed(() => {
let _empty = {
page_container: 'page_container',
layout_container: 'layout_container',
};
switch (props.layout) {
case 'Full_Page':
_empty = {
page_container: 'page_container full-size-page',
layout_container: 'layout_container full-size-layout',
};
break;
case 'Center_Page':
_empty = {
page_container: 'page_container full-size-page',
layout_container: 'layout_container center-layout',
};
break;
case 'Background_Page':
_empty = {
page_container: 'page_container full-size-page background-container',
layout_container: 'layout_container center-layout',
};
break;
default:
break;
}
return _empty;
})
</script>
<template>
<div class="h-100 overflow-y-auto">
<HeaderHomeTemplate />
<div :class="[classForLayout.page_container]">
<div :class="[classForLayout.layout_container]">
<slot />
</div>
</div>
<FooterHomeTemplate />
</div>
</template>
<style lang="scss" scoped>
.body-center {
padding: 40px 0px;
}
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding: 0 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
</style>
@@ -0,0 +1 @@
export { default as Article_Section_Default } from './articles/Default.vue'
@@ -0,0 +1,52 @@
<script lang="ts" setup>
import { Article_Section_Default } from './index';
import { enumPageSectionLayouts } from "@/definitions/enum";
const _props = defineProps<{
settings?: any,
content?: any
section: any
}>()
const definedDynamicSection: Record<string, any> = {
'Article_Default': Article_Section_Default,
[`Article_${enumPageSectionLayouts.VERTICAL_TWO}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.VERTICAL_LEFT_TWO}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.VERTICAL_RIGHT_TWO}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.VERTICAL_THREE}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.VERTICAL_FOUR}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_ONE}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_TWO}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_THREE}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_FOUR}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_FIVE}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_SIX}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_SEVEN}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_EIGHT}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_NINE}`]: Article_Section_Default,
[`Article_${enumPageSectionLayouts.HORIZONTAL_TEN}`]: Article_Section_Default,
}
const getCurrentSection = computed(() => `${_props.settings.template}_${_props.settings.layout}`);
const GET_PROPS = computed(() => {
return () => {
let props: any = {};
if (_props.settings) {
for (const [key, value] of _props.settings ? Object.entries(_props.settings) : []) {
props = {
...props,
[key]: value
}
}
}
return props;
};
})
</script>
<template>
<component :is="definedDynamicSection[getCurrentSection] || null" v-bind="{ ...(GET_PROPS()), section: _props.section, content: _props.content, settings: _props.settings }">
<slot />
</component>
</template>
@@ -0,0 +1,72 @@
<script setup lang="ts">
const props = defineProps<{
layout?: any
}>()
const CLASS_FOR_LAYOUT = computed(() => {
let _classForLayout = {};
switch (props.layout) {
case 'Full_Page':
_classForLayout = {
page_container: 'page_container full-size-page',
layout_container: 'layout_container full-size-layout',
};
break;
case 'Center_Page':
_classForLayout = {
page_container: 'page_container full-size-page',
layout_container: 'layout_container center-layout',
};
break;
case 'Background_Page':
_classForLayout = {
page_container: 'page_container full-size-page background-container',
layout_container: 'layout_container center-layout',
};
break;
default:
_classForLayout = {
page_container: 'page_container',
layout_container: 'layout_container',
};
break;
}
return _classForLayout;
})
</script>
<template>
<div :class="[CLASS_FOR_LAYOUT.page_container]">
<div :class="[CLASS_FOR_LAYOUT.layout_container]" class="grid-container">
<slot />
</div>
</div>
</template>
<style lang="scss" scoped>
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding-left: 20px;
padding-right: 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
.grid-container {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 40px;
}
</style>
@@ -0,0 +1,32 @@
<script setup lang="ts">
</script>
<template>
<div class="page_container full-size-page">
<div class="layout_container center-layout grid-container">
<slot />
</div>
</div>
</template>
<style lang="scss" scoped>
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding-left: 20px;
padding-right: 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
</style>
@@ -0,0 +1,42 @@
/*
- LayoutType: None=0 | Normal=1 | Short=2 | Long=3 | Page=4
- ContentType: None=0 | General=1 | Photo=2 | Audio=3 | Video=4 | Graphic=5 | Document=6 | Interaction=7 (Poll,Quiz)
*/
<script setup lang="ts">
</script>
<template>
<div class="page_container full-size-page">
<div class="layout_container full-size-layout grid-container">
<slot />
</div>
</div>
</template>
<style lang="scss" scoped>
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding-left: 20px;
padding-right: 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
.grid-container {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 40px;
}
</style>
@@ -0,0 +1,32 @@
<script setup lang="ts">
</script>
<template>
<div class="page_container full-size-page">
<div class="layout_container center-layout grid-container">
<slot />
</div>
</div>
</template>
<style lang="scss" scoped>
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding-left: 20px;
padding-right: 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
</style>
@@ -0,0 +1,42 @@
/*
- LayoutType: None=0 | Normal=1 | Short=2 | Long=3 | Page=4
- ContentType: None=0 | General=1 | Photo=2 | Audio=3 | Video=4 | Graphic=5 | Document=6 | Interaction=7 (Poll,Quiz)
*/
<script setup lang="ts">
</script>
<template>
<div class="page_container full-size-page">
<div class="layout_container full-size-layout grid-container">
<slot />
</div>
</div>
</template>
<style lang="scss" scoped>
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding-left: 20px;
padding-right: 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
.grid-container {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 40px;
}
</style>
@@ -0,0 +1,32 @@
<script setup lang="ts">
</script>
<template>
<div class="page_container full-size-page">
<div class="layout_container center-layout grid-container">
<slot />
</div>
</div>
</template>
<style lang="scss" scoped>
.page_container {
&.full-size-page {
width: 100%;
}
.full-size-layout {
padding-left: 20px;
padding-right: 20px;
}
}
.layout_container {
padding-top: 40px;
padding-bottom: 40px;
&.center-layout {
max-width: 1300px;
margin: auto;
}
}
</style>
@@ -0,0 +1,8 @@
export { default as BASE_LAYOUT } from './Default.vue'
// Article
export { default as ARTICLE_LONG_LAYOUT } from './articles/Long.vue'
export { default as ARTICLE_NONE_LAYOUT } from './articles/None.vue'
export { default as ARTICLE_NORMAL_LAYOUT } from './articles/Normal.vue'
export { default as ARTICLE_PAGE_LAYOUT } from './articles/Page.vue'
export { default as ARTICLE_SHORT_LAYOUT } from './articles/Short.vue'
@@ -0,0 +1,50 @@
<script lang="ts" setup>
import { layouts } from "@/definitions/enum";
import {
BASE_LAYOUT,
ARTICLE_SHORT_LAYOUT,
ARTICLE_PAGE_LAYOUT,
ARTICLE_NORMAL_LAYOUT,
ARTICLE_NONE_LAYOUT,
ARTICLE_LONG_LAYOUT,
} from './index';
const _props = defineProps<{
settings?: any,
}>()
const definedDynamicPageLayout: Record<string, any> = {
'Default': BASE_LAYOUT,
[layouts.FULL_PAGE]: BASE_LAYOUT,
[layouts.CENTER_PAGE]: BASE_LAYOUT,
[layouts.BACKGROUND_PAGE]: BASE_LAYOUT,
'ARTICLE_SHORT': ARTICLE_SHORT_LAYOUT,
'ARTICLE_PAGE': ARTICLE_PAGE_LAYOUT,
'ARTICLE_NORMAL': ARTICLE_NORMAL_LAYOUT,
'ARTICLE_NONE': ARTICLE_NONE_LAYOUT,
'ARTICLE_LONG': ARTICLE_LONG_LAYOUT,
}
const getCurrentLayout = computed(() => _props.settings && _props.settings.layout);
const GET_PROPS = computed(() => {
return () => {
let props: any = {};
for (const [key, value] of _props.settings ? Object.entries(_props.settings) : []) {
props = {
...props,
[key]: value
}
}
return props;
};
})
</script>
<template>
<component :is="definedDynamicPageLayout[getCurrentLayout] || null" v-bind="GET_PROPS()">
<slot />
</component>
</template>
@@ -0,0 +1,19 @@
<script setup lang="ts">
import DynamicLayout from '~/components/dynamic-page/page/layouts/index.vue';
import HeaderHomeTemplate from '~/components/dynamic-page/page/templates/components/headers/HeaderHomeTemplate.vue'
import FooterHomeTemplate from '~/components/dynamic-page/page/templates/components/footers/FooterHomeTemplate.vue'
const props = defineProps<{
settings?: any
}>()
</script>
<template>
<div class="h-100 overflow-y-auto">
<HeaderHomeTemplate />
<DynamicLayout :settings="props.settings">
<slot />
</DynamicLayout>
<FooterHomeTemplate />
</div>
</template>
@@ -0,0 +1,237 @@
<script setup lang="ts">
</script>
<template>
<div>
useCmsPageStore
</div>
</template>
<style lang="scss" scoped>
.col-span-8 {
grid-column: span 8 / span 8;
@media (max-width: 1150px) {
grid-column: span 7 / span 7;
}
}
.col-span-12 {
grid-column: span 12 / span 12 !important;
}
.mbootom-5 {
margin-bottom: 5px;
}
.mbootom-14 {
margin-bottom: 14px;
}
.text-neutral-500 {
color: #737373;
}
.grid-col-2 {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
&.grid-col-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
}
.flex-col {
display: flex;
flex-direction: column;
.text-span {
font-size: 1rem;
line-height: 1.75rem;
font-weight: 700;
letter-spacing: 0.025em;
flex: 1;
word-break: break-word;
}
.text-a {
font-size: 0.8rem;
line-height: 1rem;
}
}
.lg-row {
@media (min-width: 1300px) {
flex-direction: row;
justify-content: space-between;
}
}
.footer1 {
margin-top: 1.5rem;
background-color: #ffffff;
color: black;
border-top: 1px solid #bfbfbf;
&-wrap {
max-width: 90%;
margin: auto;
padding-top: 1rem;
padding-left: 0;
padding-right: 0;
.section-right {
display: grid;
margin-bottom: 0.5rem;
gap: 1rem;
font-size: 0.8rem;
line-height: 1.25rem;
font-weight: 400;
@media (min-width: 950px) {
grid-template-columns: repeat(12, minmax(0, 1fr));
}
.footer-category {
display: grid;
gap: 1rem;
height: 100%;
/* grid-template-columns: repeat(5, minmax(0, 1fr)); */
&.grid-col-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
&.grid-col-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.item-nav {
padding: 10px;
.text {
font-size: 0.8rem;
line-height: 1.25rem;
color: black;
}
}
.drag-new {
display: flex;
justify-content: center;
align-items: center;
border-radius: 6px;
background: #215486;
font-size: 40px;
color: #fff;
margin: 0 11px;
max-width: 200px;
height: 30px;
}
}
&-4 {
display: grid;
grid-column: span 4 / span 4;
grid-auto-rows: max;
gap: 1rem;
&.border-top-left-0 {
border-left: 0;
border-top: 1px solid #bfbfbf;
padding-top: 1rem;
}
@media (max-width: 1150px) {
grid-column: span 5 / span 5;
}
@media (min-width: 950px) {
padding-left: 1rem;
border-left: 1px solid #bfbfbf;
}
.text {
margin-bottom: 0.5rem;
font-size: 1rem;
line-height: 1.75rem;
font-weight: 700;
text-transform: uppercase;
&-wrap {
display: flex;
flex-direction: column;
gap: 0.75rem;
white-space: nowrap;
.text-item {
display: flex;
gap: 0.5rem;
align-items: center;
max-width: 100%;
.text-child {
font-size: 0.8rem;
line-height: 1.25rem;
flex: 1;
word-break: break-word;
}
}
}
}
}
}
.section-bottom {
display: flex;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
flex-direction: column;
gap: 1rem;
justify-content: space-between;
align-items: center;
@media (min-width: 640px) {
flex-direction: row;
}
.ssr {
display: flex;
gap: 1rem;
justify-content: center;
align-items: center;
a {
font-size: 0.8rem;
color: #737373;
line-height: 1.25rem;
@media (min-width: 1024px) {
font-size: 1rem;
line-height: 1.5rem;
}
}
}
&__left {
display: flex;
justify-content: center;
align-items: center;
@media (min-width: 640px) {
order: 1;
}
}
&__right {
display: flex;
gap: 1rem;
justify-content: center;
align-items: center;
.icon1 {
color: #737373;
display: grid;
place-items: center;
border-radius: 9999px;
border: 1px solid #737373;
transition-duration: 300ms;
width: 32px;
height: 32px;
}
@media (min-width: 640px) {
order: 3;
}
}
}
}
}
</style>
@@ -0,0 +1,237 @@
<script setup lang="ts">
</script>
<template>
<div>
useCmsPageStore
</div>
</template>
<style lang="scss" scoped>
.col-span-8 {
grid-column: span 8 / span 8;
@media (max-width: 1150px) {
grid-column: span 7 / span 7;
}
}
.col-span-12 {
grid-column: span 12 / span 12 !important;
}
.mbootom-5 {
margin-bottom: 5px;
}
.mbootom-14 {
margin-bottom: 14px;
}
.text-neutral-500 {
color: #737373;
}
.grid-col-2 {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
&.grid-col-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
}
.flex-col {
display: flex;
flex-direction: column;
.text-span {
font-size: 1rem;
line-height: 1.75rem;
font-weight: 700;
letter-spacing: 0.025em;
flex: 1;
word-break: break-word;
}
.text-a {
font-size: 0.8rem;
line-height: 1rem;
}
}
.lg-row {
@media (min-width: 1300px) {
flex-direction: row;
justify-content: space-between;
}
}
.footer1 {
margin-top: 1.5rem;
background-color: #ffffff;
color: black;
border-top: 1px solid #bfbfbf;
&-wrap {
max-width: 90%;
margin: auto;
padding-top: 1rem;
padding-left: 0;
padding-right: 0;
.section-right {
display: grid;
margin-bottom: 0.5rem;
gap: 1rem;
font-size: 0.8rem;
line-height: 1.25rem;
font-weight: 400;
@media (min-width: 950px) {
grid-template-columns: repeat(12, minmax(0, 1fr));
}
.footer-category {
display: grid;
gap: 1rem;
height: 100%;
/* grid-template-columns: repeat(5, minmax(0, 1fr)); */
&.grid-col-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
&.grid-col-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.item-nav {
padding: 10px;
.text {
font-size: 0.8rem;
line-height: 1.25rem;
color: black;
}
}
.drag-new {
display: flex;
justify-content: center;
align-items: center;
border-radius: 6px;
background: #215486;
font-size: 40px;
color: #fff;
margin: 0 11px;
max-width: 200px;
height: 30px;
}
}
&-4 {
display: grid;
grid-column: span 4 / span 4;
grid-auto-rows: max;
gap: 1rem;
&.border-top-left-0 {
border-left: 0;
border-top: 1px solid #bfbfbf;
padding-top: 1rem;
}
@media (max-width: 1150px) {
grid-column: span 5 / span 5;
}
@media (min-width: 950px) {
padding-left: 1rem;
border-left: 1px solid #bfbfbf;
}
.text {
margin-bottom: 0.5rem;
font-size: 1rem;
line-height: 1.75rem;
font-weight: 700;
text-transform: uppercase;
&-wrap {
display: flex;
flex-direction: column;
gap: 0.75rem;
white-space: nowrap;
.text-item {
display: flex;
gap: 0.5rem;
align-items: center;
max-width: 100%;
.text-child {
font-size: 0.8rem;
line-height: 1.25rem;
flex: 1;
word-break: break-word;
}
}
}
}
}
}
.section-bottom {
display: flex;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
flex-direction: column;
gap: 1rem;
justify-content: space-between;
align-items: center;
@media (min-width: 640px) {
flex-direction: row;
}
.ssr {
display: flex;
gap: 1rem;
justify-content: center;
align-items: center;
a {
font-size: 0.8rem;
color: #737373;
line-height: 1.25rem;
@media (min-width: 1024px) {
font-size: 1rem;
line-height: 1.5rem;
}
}
}
&__left {
display: flex;
justify-content: center;
align-items: center;
@media (min-width: 640px) {
order: 1;
}
}
&__right {
display: flex;
gap: 1rem;
justify-content: center;
align-items: center;
.icon1 {
color: #737373;
display: grid;
place-items: center;
border-radius: 9999px;
border: 1px solid #737373;
transition-duration: 300ms;
width: 32px;
height: 32px;
}
@media (min-width: 640px) {
order: 3;
}
}
}
}
}
</style>
@@ -0,0 +1,132 @@
<svg `viewBox="0 0 693 315" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="693" height="315" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0586 26.0907H76.5475L89.0139 54.0907H25.525L13.0586 26.0907Z"
fill="#262626" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M25.525 54.0907L13.0586 26.0907H76.5475L89.0139 54.0907L139.685 167.901L107.941 239.2L25.525 54.0907Z"
fill="#262626" />
<path d="M245 77H289.5C304.136 77 316 88.8645 316 103.5C316 118.136 304.136 130 289.5 130H245V77Z" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M245 77H244.036H243.66L243.214 78H245V77Z" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M294.88 99.6374L244.036 77H243.66L243.214 78L183.966 211.074L235.124 233.852L294.88 99.6374Z" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M202.823 26.0907H266.312L253.846 54.0907H190.357L194 45.9081L202.823 26.0907Z" fill="#EC3E36" />
<path d="M107.941 239.2L139.685 310.499L171.43 239.2L139.685 167.901L107.941 239.2Z" fill="#EC3E36" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M202.823 26.0907L194 45.9081V179.091H303.5C345.75 179.091 380 144.841 380 102.591C380 60.3409 345.75 26.0907 303.5 26.0907L202.823 26.0907Z"
fill="#EC3E36" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M171.43 239.2L139.685 167.901L190.357 54.0907L194 45.9081L202.823 26.0907H266.312L253.846 54.0907L171.43 239.2Z"
fill="#EC3E36" />
<path d="M245 77H289.5C304.136 77 316 88.8645 316 103.5V103.5C316 118.136 304.136 130 289.5 130H245V77Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M245 77H244.036H243.66L243.214 78H245V77Z" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M294.88 99.6374L244.036 77H243.66L243.214 78L183.966 211.074L235.124 233.852L294.88 99.6374Z" fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M202.823 26.0907H266.312L253.846 54.0907H190.357L194 45.9081L202.823 26.0907Z" fill="#EC3E36" />
<path d="M107.941 239.2L139.685 310.499L171.43 239.2L139.685 167.901L107.941 239.2Z" fill="#EC3E36" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M202.823 26.0907L194 45.9081V179.091H303.5C345.75 179.091 380 144.841 380 102.591C380 60.3409 345.75 26.0907 303.5 26.0907L202.823 26.0907Z"
fill="#EC3E36" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M171.43 239.2L139.685 167.901L190.357 54.0907L194 45.9081L202.823 26.0907H266.312L253.846 54.0907L171.43 239.2Z"
fill="#EC3E36" />
<path
d="M245.034 77.1399H289.534C304.17 77.1399 316.034 89.0043 316.034 103.64V103.64C316.034 118.275 304.17 130.14 289.534 130.14H245.034V77.1399Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M245.034 77.1398H244.07H243.694L243.249 78.1398H245.034V77.1398Z"
fill="white" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M294.915 99.7773L244.07 77.1398H243.694L243.249 78.1398L184 211.214L235.159 233.992L294.915 99.7773Z"
fill="white" />
<path d="M205 209.322L225.941 209.322L244.5 281.094L226.778 281.127L205 209.322Z" fill="#1A1A1A" />
<path d="M269.491 208.806L248.379 208.829L232 281.092L249.646 281.116L269.491 208.806Z" fill="#1A1A1A" />
<path d="M327 209.195H345V281.195H327V209.195Z" fill="#1A1A1A" />
<rect x="358" y="208.431" width="21" height="73" fill="#1A1A1A" />
<path
d="M376 208.431H387.5C401.031 208.431 412 219.4 412 232.931V232.931C412 246.462 401.031 257.431 387.5 257.431H376V208.431Z"
fill="#1A1A1A" />
<path
d="M379 225.431H385.5C389.642 225.431 393 228.789 393 232.931V232.931C393 237.073 389.642 240.431 385.5 240.431H379V225.431Z"
fill="white" />
<rect x="425" y="208.431" width="19" height="73" fill="#1A1A1A" />
<path
d="M443 208.431H459.5C473.031 208.431 484 219.4 484 232.931V232.931C484 246.462 473.031 257.431 459.5 257.431H443V208.431Z"
fill="#1A1A1A" />
<path
d="M444 225.431H456.5C460.642 225.431 464 228.789 464 232.931V232.931C464 237.073 460.642 240.431 456.5 240.431H444V225.431Z"
fill="white" />
<path d="M450.564 251.949L468.306 242.001L484.868 281.225L462.462 281.225L450.564 251.949Z" fill="#1A1A1A" />
<rect x="497" y="208.431" width="20" height="73" fill="#1A1A1A" />
<rect x="507" y="208.431" width="46" height="19" fill="#1A1A1A" />
<rect x="512" y="263.431" width="41" height="18" fill="#1A1A1A" />
<rect x="514" y="237.431" width="29" height="16" fill="#1A1A1A" />
<rect x="571" y="266.437" width="40" height="14" rx="7" fill="#0B5398" />
<path d="M566 263.437H604V281.437H566V263.437Z" fill="#1A1A1A" />
<ellipse cx="603.5" cy="259.437" rx="19.5" ry="22" fill="#1A1A1A" />
<rect x="580" y="225.437" width="25" height="12" rx="6" fill="#F5F5F5" />
<rect x="570" y="252.437" width="34" height="11" rx="5.5" fill="white" />
<rect x="589" y="238.437" width="20" height="13" rx="6.5" fill="#0B5398" />
<path d="M618.611 226.028L585.748 226.009L585.757 208.431L618.62 208.45L618.611 226.028Z" fill="#1A1A1A" />
<ellipse cx="19.3494" cy="22.0044" rx="19.3494" ry="22.0044"
transform="matrix(-1 -0.000571404 0.000499233 -1 605.053 252.474)" fill="#1A1A1A" />
<path
d="M610.184 237.45L590.914 237.439C587.759 237.437 585.202 234.878 585.204 231.723V231.723C585.205 228.568 587.764 226.011 590.92 226.013L610.189 226.024L610.184 237.45Z"
fill="white" />
<rect width="22.315" height="13.9922" rx="6.99612"
transform="matrix(-1 -0.000571404 0.000499233 -1 609.698 252.372)" fill="#1A1A1A" />
<rect x="641" y="266.437" width="40" height="14" rx="7" fill="#0B5398" />
<path d="M636 263.437H674V281.437H636V263.437Z" fill="#1A1A1A" />
<ellipse cx="673.5" cy="259.437" rx="19.5" ry="22" fill="#1A1A1A" />
<rect x="650" y="225.437" width="25" height="12" rx="6" fill="#F5F5F5" />
<rect x="640" y="252.437" width="34" height="11" rx="5.5" fill="white" />
<rect x="659" y="238.437" width="20" height="13" rx="6.5" fill="#0B5398" />
<path d="M688.611 226.028L655.748 226.009L655.757 208.431L688.62 208.45L688.611 226.028Z" fill="#1A1A1A" />
<ellipse cx="19.3494" cy="22.0044" rx="19.3494" ry="22.0044"
transform="matrix(-1 -0.000571404 0.000499233 -1 675.053 252.474)" fill="#1A1A1A" />
<path
d="M680.184 237.45L660.914 237.439C657.759 237.437 655.202 234.878 655.204 231.723V231.723C655.205 228.568 657.764 226.011 660.92 226.013L680.189 226.024L680.184 237.45Z"
fill="white" />
<rect width="22.315" height="13.9922" rx="6.99612"
transform="matrix(-1 -0.000571404 0.000499233 -1 679.698 252.372)" fill="#1A1A1A" />
<path d="M282 209.195H299.152H300V281.195H282V209.195Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M327 280.195H326V280.946L326.179 281.195H327V280.195Z"
fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M299.152 209.195L284.621 223.419L326 280.946L326.179 281.195L326.182 281.2L341.494 266.211L301 209.915L300.483 209.195H300H299.152Z"
fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M299.152 209.195H299V210.195H301V209.915L300.483 209.195H299.152Z"
fill="#1A1A1A" />
<rect x="299.737" y="208.182" width="3.7116" height="3.43404" transform="rotate(-36 299.737 208.182)"
fill="white" />
<rect x="302" y="207.431" width="25" height="3" fill="white" />
<rect x="282" y="280.431" width="18" height="1" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M345 280.431H326V280.948L326.351 281.431H326.663H345V280.431Z"
fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M326.611 280.233L325.871 280.77L326 280.948L326.351 281.431H326.663L327.198 281.042L326.611 280.233Z"
fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M227.195 279.423L226.345 279.683L226.574 280.431L226.88 281.431H227.469L227.78 281.336L227.195 279.423Z"
fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M226.574 280.431H249.833L249.56 281.431H248.181H227.469H226.88L226.574 280.431Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M250.01 279.783L248.727 279.433L248.181 281.431H249.56L249.833 280.431L250.01 279.783Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M206.271 210.241L205.717 208.431H204.729L205.034 209.431L205.367 210.518L206.271 210.241Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M224.233 208.431L224.653 209.955L226.004 209.583L225.962 209.431L225.686 208.431H224.233Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M249.055 211.213L249.697 208.431H248.481L248.25 209.431L247.9 210.946L249.055 211.213Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M268.585 208.431L268 210.344L268.956 210.636L269.325 209.431L269.63 208.431H268.585Z" fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd"
d="M249.697 208.431H248.481L248.25 209.431H269.325L269.63 208.431H268.585H249.697ZM224.233 208.431H225.686L225.962 209.431H205.034L204.729 208.431H205.717H224.233Z"
fill="#1A1A1A" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M282 208.431H299.922L300.648 209.431H282V208.431Z"
fill="#1A1A1A" />
<path d="M301 209.431V208.431H299.922L300.648 209.431H301Z" fill="white" />
<rect x="327" y="208.431" width="18" height="1" fill="#1A1A1A" />
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

@@ -0,0 +1,19 @@
<script setup lang="ts">
import DynamicLayout from '~/components/dynamic-page/page/layouts/index.vue';
import HeaderHomeTemplate from '~/components/dynamic-page/page/templates/components/headers/HeaderHomeTemplate.vue'
import FooterHomeTemplate from '~/components/dynamic-page/page/templates/components/footers/FooterHomeTemplate.vue'
const props = defineProps<{
settings?: any
}>()
</script>
<template>
<div>
<HeaderHomeTemplate />
<DynamicLayout :settings="props.settings">
<slot />
</DynamicLayout>
<FooterHomeTemplate />
</div>
</template>
@@ -0,0 +1 @@
export { default as HomeBasic } from './homes/Basic.vue'
@@ -0,0 +1,36 @@
<script lang="ts" setup>
import { HomeBasic } from './index';
const _props = defineProps<{
settings: any
}>()
const definedDynamicPage: Record<string, any> = {
'Home' : HomeBasic,
}
const getCurrentTemplate = computed(() => {
return _props.settings && _props.settings.template || '';
});
const GET_PROPS = computed(() => {
return () => {
let props : any = {};
if (_props.settings) {
for (const [key, value] of _props.settings ? Object.entries(_props.settings) : []) {
props = {
...props,
[key]: value
}
}
}
return props;
};
})
</script>
<template>
<component :is="definedDynamicPage[getCurrentTemplate] || null" v-bind="{...(GET_PROPS()), settings: _props.settings}">
<slot />
</component>
</template>