feat. GR_CONTENTS_01 템플릿 제작
This commit is contained in:
@@ -15,15 +15,18 @@
|
||||
}
|
||||
|
||||
/* Title Utility Classes */
|
||||
.title-lg {
|
||||
.title-xlg {
|
||||
@apply line-clamp-4 text-[24px] font-[700] leading-[34px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:line-clamp-3 md:text-[50px] md:leading-[70px];
|
||||
}
|
||||
.title-md {
|
||||
.title-lg {
|
||||
@apply line-clamp-4 text-[20px] font-[700] leading-[30px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:line-clamp-3 md:text-[42px] md:leading-[56px];
|
||||
}
|
||||
.title-sm {
|
||||
.title-md {
|
||||
@apply line-clamp-2 text-[16px] font-[500] leading-[24px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:line-clamp-1 md:text-[24px] md:leading-[34px];
|
||||
}
|
||||
.title-sm {
|
||||
@apply text-[15px] font-[500] leading-[24px] tracking-[-0.45px] md:text-[20px] md:leading-[30px] md:tracking-[-0.6px];
|
||||
}
|
||||
.title-xs {
|
||||
@apply text-[14px] font-[500] leading-[20px] tracking-[-0.42px] md:text-[18px] md:leading-[26px] md:tracking-[-0.54px];
|
||||
}
|
||||
|
||||
62
layers/components/atoms/Img.vue
Normal file
62
layers/components/atoms/Img.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { getResolvedHost } from '#layers/utils/styleUtil'
|
||||
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
||||
|
||||
interface Props {
|
||||
resourcesData?: PageDataResourceGroup
|
||||
objectFit?: 'contain' | 'cover'
|
||||
alt?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
objectFit: 'contain',
|
||||
})
|
||||
|
||||
const imagePaths = computed(() => {
|
||||
if (!props.resourcesData?.res_path) return null
|
||||
|
||||
const pcPath =
|
||||
props.resourcesData.res_path.path_pc ?? props.resourcesData.res_path.path_mo
|
||||
const moPath =
|
||||
props.resourcesData.res_path.path_mo ?? props.resourcesData.res_path.path_pc
|
||||
|
||||
return {
|
||||
pc: pcPath ? getResolvedHost(pcPath) : '',
|
||||
mo: moPath ? getResolvedHost(moPath) : '',
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<picture v-if="imagePaths">
|
||||
<source media="(min-width: 1024px)" :srcset="imagePaths.pc" />
|
||||
<source media="(max-width: 1023px)" :srcset="imagePaths.mo" />
|
||||
<img
|
||||
:src="imagePaths.pc"
|
||||
:alt="alt"
|
||||
:class="`w-full h-full object-${objectFit}`"
|
||||
loading="lazy"
|
||||
/>
|
||||
</picture>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 이미지 깨짐 시 보더 및 아이콘 제거 */
|
||||
img {
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 깨진 이미지 아이콘과 alt 텍스트 숨김 */
|
||||
img::before,
|
||||
img::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* alt 텍스트 영역 숨김 */
|
||||
img[alt] {
|
||||
text-indent: -9999px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@@ -17,9 +17,9 @@ const imagePaths = computed(() => {
|
||||
if (!props.resourcesData?.res_path) return null
|
||||
|
||||
const pcPath =
|
||||
props.resourcesData.res_path.path_pc || props.resourcesData.res_path.path_mo
|
||||
props.resourcesData.res_path.path_pc ?? props.resourcesData.res_path.path_mo
|
||||
const moPath =
|
||||
props.resourcesData.res_path.path_mo || props.resourcesData.res_path.path_pc
|
||||
props.resourcesData.res_path.path_mo ?? props.resourcesData.res_path.path_pc
|
||||
|
||||
return {
|
||||
pc: pcPath ? getResolvedHost(pcPath) : '',
|
||||
@@ -45,8 +45,8 @@ const sanitizedContent = computed(() => {
|
||||
<template>
|
||||
<!-- 이미지 -->
|
||||
<picture v-if="isTypeImage(resourcesData?.resource_type) && imagePaths">
|
||||
<source media="(min-width: 1024px)" :srcset="imagePaths.pc" />
|
||||
<source media="(max-width: 1023px)" :srcset="imagePaths.mo" />
|
||||
<source :srcset="imagePaths.pc" media="(min-width: 1024px)" />
|
||||
<source :srcset="imagePaths.mo" media="(max-width: 1023px)" />
|
||||
<img
|
||||
:src="imagePaths.pc"
|
||||
:alt="alt || displayText"
|
||||
|
||||
@@ -6,40 +6,27 @@ const { fullLoading } = storeToRefs(loadingStore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition
|
||||
enter-active-class="transition-opacity duration-300 ease-in-out"
|
||||
enter-from-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-active-class="transition-opacity duration-300 ease-in-out"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<div
|
||||
v-if="fullLoading"
|
||||
class="fixed inset-0 bg-black/80 flex items-center justify-center z-[9999]"
|
||||
>
|
||||
<!-- 메인 스피너 -->
|
||||
<div class="relative w-16 h-16">
|
||||
<!-- 외부 링 -->
|
||||
<div
|
||||
class="absolute inset-0 border-4 border-transparent border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
<!-- 중간 링 -->
|
||||
<div
|
||||
class="absolute inset-1 border-4 border-transparent border-t-purple-500 rounded-full animate-spin"
|
||||
style="animation-delay: -0.3s"
|
||||
/>
|
||||
<!-- 내부 링 -->
|
||||
<div
|
||||
class="absolute inset-2 border-4 border-transparent border-t-cyan-500 rounded-full animate-spin"
|
||||
style="animation-delay: -0.6s"
|
||||
/>
|
||||
<!-- 중심 링 -->
|
||||
<div
|
||||
class="absolute inset-3 border-4 border-transparent border-t-emerald-500 rounded-full animate-spin"
|
||||
style="animation-delay: -0.9s"
|
||||
/>
|
||||
</div>
|
||||
<Transition name="fade">
|
||||
<div v-if="fullLoading" class="spinner-container">
|
||||
<div class="spinner-line"></div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.spinner-container {
|
||||
@apply fixed inset-0 bg-black/90 flex items-center justify-center z-[900];
|
||||
}
|
||||
.spinner {
|
||||
@apply w-[80px] h-[80px] bg-cover bg-center bg-no-repeat bg-[url('/images/common/publisning_template_loader_black.png')];
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
.spinner-container {
|
||||
@apply bg-white/90;
|
||||
}
|
||||
.spinner {
|
||||
@apply bg-[url('/images/common/publisning_template_loader_white.png')];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,42 +14,29 @@ const canTeleport = (localId: string) => {
|
||||
<template>
|
||||
<template v-for="[localId, loadingInfo] in localLoadings" :key="localId">
|
||||
<Teleport v-if="canTeleport(localId)" :to="`#${localId}`">
|
||||
<Transition
|
||||
enter-active-class="transition-opacity duration-300 ease-in-out"
|
||||
enter-from-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-active-class="transition-opacity duration-300 ease-in-out"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<div
|
||||
v-if="loadingInfo.active"
|
||||
class="fixed inset-0 bg-black/80 flex items-center justify-center z-[9999]"
|
||||
>
|
||||
<!-- 메인 스피너 -->
|
||||
<div class="relative w-16 h-16">
|
||||
<!-- 외부 링 -->
|
||||
<div
|
||||
class="absolute inset-0 border-4 border-transparent border-t-blue-500 rounded-full animate-spin"
|
||||
/>
|
||||
<!-- 중간 링 -->
|
||||
<div
|
||||
class="absolute inset-1 border-4 border-transparent border-t-purple-500 rounded-full animate-spin"
|
||||
style="animation-delay: -0.3s"
|
||||
/>
|
||||
<!-- 내부 링 -->
|
||||
<div
|
||||
class="absolute inset-2 border-4 border-transparent border-t-cyan-500 rounded-full animate-spin"
|
||||
style="animation-delay: -0.6s"
|
||||
/>
|
||||
<!-- 중심 링 -->
|
||||
<div
|
||||
class="absolute inset-3 border-4 border-transparent border-t-emerald-500 rounded-full animate-spin"
|
||||
style="animation-delay: -0.9s"
|
||||
/>
|
||||
</div>
|
||||
<Transition name="fade">
|
||||
<div v-if="loadingInfo.active" class="spinner-container">
|
||||
<div class="spinner-line"></div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.spinner-container {
|
||||
@apply fixed inset-0 bg-black/90 flex items-center justify-center z-[900];
|
||||
}
|
||||
.spinner {
|
||||
@apply w-[80px] h-[80px] bg-cover bg-center bg-no-repeat bg-[url('/images/common/publisning_template_loader_black.png')];
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
.spinner-container {
|
||||
@apply bg-white/90;
|
||||
}
|
||||
.spinner {
|
||||
@apply bg-[url('/images/common/publisning_template_loader_white.png')];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useSplideArrow } from '#layers/composables/useSplideArrow'
|
||||
interface Props {
|
||||
slideItemSize: SlideItemSize
|
||||
slideItemLength?: number
|
||||
autoplay?: boolean | string
|
||||
autoplay?: boolean
|
||||
arrows?: boolean
|
||||
pagination?: boolean
|
||||
class?: string
|
||||
@@ -24,9 +24,7 @@ const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
||||
// Splide 화살표 로직을 위한 composable 사용
|
||||
const { addArrowClickListeners } = useSplideArrow()
|
||||
|
||||
const isMultipleItems = computed(() => {
|
||||
return props.slideItemLength > 1
|
||||
})
|
||||
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
||||
|
||||
const options = computed((): ResponsiveOptions => {
|
||||
return {
|
||||
@@ -106,7 +104,7 @@ const handleMove = (
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="`center-highlight ${props.class || ''}`" :style="style">
|
||||
<div :class="`center-focus ${props.class || ''}`" :style="style">
|
||||
<Splide
|
||||
:options="options"
|
||||
@splide:mounted="handleSplideMounted"
|
||||
@@ -118,58 +116,58 @@ const handleMove = (
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.center-highlight {
|
||||
.center-focus {
|
||||
@apply w-full;
|
||||
}
|
||||
.center-highlight:deep(.splide__slide) {
|
||||
.center-focus:deep(.splide__slide) {
|
||||
@apply flex items-center justify-center;
|
||||
width: var(--banner-width-mo);
|
||||
height: var(--banner-height-mo-active);
|
||||
margin-right: var(--banner-gap-mo);
|
||||
opacity: 0.5;
|
||||
}
|
||||
.center-highlight:deep(.splide__slide) .slide-inner {
|
||||
.center-focus:deep(.splide__slide) .slide-inner {
|
||||
width: var(--banner-width-mo);
|
||||
height: var(--banner-height-mo);
|
||||
opacity: 0.5;
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) {
|
||||
.center-focus:deep(.splide__slide.is-active) {
|
||||
width: var(--banner-width-mo-container);
|
||||
opacity: 1;
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) .slide-inner {
|
||||
.center-focus:deep(.splide__slide.is-active) .slide-inner {
|
||||
width: var(--banner-width-mo-active);
|
||||
height: var(--banner-height-mo-active);
|
||||
opacity: 1;
|
||||
transition: all 0.45s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-next),
|
||||
.center-highlight:deep(.splide__slide.is-prev) {
|
||||
.center-focus:deep(.splide__slide.is-next),
|
||||
.center-focus:deep(.splide__slide.is-prev) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* PC 스타일 */
|
||||
@media (min-width: 1024px) {
|
||||
.center-highlight:deep(.splide__slide) {
|
||||
.center-focus:deep(.splide__slide) {
|
||||
width: var(--banner-width-pc);
|
||||
height: var(--banner-height-pc-active);
|
||||
margin-right: var(--banner-gap-pc);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide) .slide-inner {
|
||||
.center-focus:deep(.splide__slide) .slide-inner {
|
||||
width: var(--banner-width-pc);
|
||||
height: var(--banner-height-pc);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) {
|
||||
.center-focus:deep(.splide__slide.is-active) {
|
||||
width: var(--banner-width-pc-container);
|
||||
}
|
||||
.center-highlight:deep(.splide__slide.is-active) .slide-inner {
|
||||
.center-focus:deep(.splide__slide.is-active) .slide-inner {
|
||||
width: var(--banner-width-pc-active);
|
||||
height: var(--banner-height-pc-active);
|
||||
}
|
||||
.center-highlight:deep(.arrow-prev) {
|
||||
.center-focus:deep(.arrow-prev) {
|
||||
left: 28px;
|
||||
}
|
||||
.center-highlight:deep(.arrow-next) {
|
||||
.center-focus:deep(.arrow-next) {
|
||||
right: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { SlideItemSize } from '#layers/types/components/slide'
|
||||
interface Props {
|
||||
slideItemSize: SlideItemSize
|
||||
slideItemLength?: number
|
||||
autoplay?: boolean | string
|
||||
autoplay?: boolean
|
||||
arrows?: boolean
|
||||
pagination?: boolean
|
||||
class?: string
|
||||
@@ -23,9 +23,7 @@ const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
||||
// Splide 화살표 로직을 위한 composable 사용
|
||||
const { addArrowClickListeners } = useSplideArrow()
|
||||
|
||||
const isMultipleItems = computed(() => {
|
||||
return props.slideItemLength > 1
|
||||
})
|
||||
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
||||
|
||||
const options = computed((): ResponsiveOptions => {
|
||||
return {
|
||||
|
||||
86
layers/components/blocks/slide/Default.vue
Normal file
86
layers/components/blocks/slide/Default.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import { Splide } from '@splidejs/vue-splide'
|
||||
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||
|
||||
interface Props {
|
||||
type?: ResponsiveOptions['type']
|
||||
slideItemLength?: number
|
||||
autoplay?: boolean
|
||||
arrows?: boolean
|
||||
pagination?: boolean
|
||||
paginationData?: PageDataResourceGroups
|
||||
breakpoints?: ResponsiveOptions['breakpoints']
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
autoplay: false,
|
||||
arrows: true,
|
||||
pagination: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
||||
|
||||
// Splide 화살표 로직을 위한 composable 사용
|
||||
const { addArrowClickListeners } = useSplideArrow()
|
||||
|
||||
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
||||
const resolvedType = computed<ResponsiveOptions['type']>(() => {
|
||||
if (props.type) return props.type
|
||||
return isMultipleItems.value ? 'loop' : 'slide'
|
||||
})
|
||||
|
||||
const options = computed((): ResponsiveOptions => {
|
||||
return {
|
||||
type: resolvedType.value,
|
||||
autoWidth: true,
|
||||
autoHeight: true,
|
||||
speed: 500,
|
||||
updateOnMove: true,
|
||||
autoplay: props.autoplay,
|
||||
arrows: props.arrows && isMultipleItems.value,
|
||||
pagination: props.pagination && isMultipleItems.value,
|
||||
classes: {
|
||||
arrows: 'splide-arrows',
|
||||
arrow: 'splide-arrow',
|
||||
prev: 'arrow-prev',
|
||||
next: 'arrow-next',
|
||||
pagination: 'splide-pagination-bullets',
|
||||
page: 'splide-pagination-bullet',
|
||||
},
|
||||
...(props.breakpoints ? { breakpoints: props.breakpoints } : {}),
|
||||
}
|
||||
})
|
||||
|
||||
const handleSplideMounted = (splide: SplideType) => {
|
||||
emit('mounted', splide)
|
||||
|
||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
||||
nextTick(() => {
|
||||
addArrowClickListeners(splide, (direction, targetIndex) => {
|
||||
emit('arrowClick', direction, targetIndex)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleMove = (
|
||||
splide: SplideType,
|
||||
newIndex: number,
|
||||
oldIndex: number,
|
||||
destIndex: number
|
||||
) => {
|
||||
emit('move', splide, newIndex, oldIndex, destIndex)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Splide
|
||||
:options="options"
|
||||
class="w-full"
|
||||
:style="getPaginationClass(props.paginationData)"
|
||||
@splide:mounted="handleSplideMounted"
|
||||
@splide:move="handleMove"
|
||||
>
|
||||
<slot />
|
||||
</Splide>
|
||||
</template>
|
||||
@@ -6,11 +6,10 @@ import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||
|
||||
interface Props {
|
||||
autoplay?: boolean | string
|
||||
autoplay?: boolean
|
||||
arrows?: boolean
|
||||
pagination?: boolean
|
||||
paginationData?: PageDataResourceGroups
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { SplideSlide } from '@splidejs/vue-splide'
|
||||
import type { OperateGroupItem } from '#layers/types/api/resourcesData'
|
||||
import type { SlideItemSize } from '#layers/types/components/slide'
|
||||
|
||||
interface BannerListProps {
|
||||
resourcesData: OperateGroupItem[]
|
||||
slideItemSize: SlideItemSize
|
||||
arrows?: boolean
|
||||
pagination?: boolean
|
||||
class?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<BannerListProps>(), {
|
||||
arrows: true,
|
||||
pagination: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BlocksSlideCenterHighlight
|
||||
:slide-item-size="props.slideItemSize"
|
||||
:slide-item-length="props.resourcesData.length"
|
||||
:pagination="false"
|
||||
class="mt-[36px] md:mt-[60px]"
|
||||
>
|
||||
<SplideSlide v-for="(item, index) in props.resourcesData" :key="index">
|
||||
<BlocksCardNews
|
||||
:title="item.title"
|
||||
:description="item.option01"
|
||||
:img-path="getResolvedHost(item.img_path)"
|
||||
:url="item.url"
|
||||
:link-target="item.link_target"
|
||||
class="slide-inner"
|
||||
/>
|
||||
</SplideSlide>
|
||||
</BlocksSlideCenterHighlight>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.center-highlight:deep(.splide__slide.is-active .card-link) {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.center-highlight:deep(.splide__slide .card-link) {
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
@@ -7,6 +7,8 @@ import GrGallery03 from '#layers/templates/GrGallery03/index.vue'
|
||||
import GrDetail01 from '#layers/templates/GrDetail01/index.vue'
|
||||
import GrDetail02 from '#layers/templates/GrDetail02/index.vue'
|
||||
import GrDetail03 from '#layers/templates/GrDetail03/index.vue'
|
||||
// import GrBoard01 from '#layers/templates/GrBoard01/index.vue'
|
||||
import GrContents01 from '#layers/templates/GrContents01/index.vue'
|
||||
|
||||
const templateRegistry = {
|
||||
GR_VISUAL_01: { component: GrVisual01 },
|
||||
@@ -19,7 +21,7 @@ const templateRegistry = {
|
||||
GR_DETAIL_01: { component: GrDetail01 },
|
||||
GR_DETAIL_02: { component: GrDetail02 },
|
||||
GR_DETAIL_03: { component: GrDetail03 },
|
||||
// GR_CONTENTS_01: { component: GrContents01 },
|
||||
GR_CONTENTS_01: { component: GrContents01 },
|
||||
} as const
|
||||
|
||||
type TemplateKey = keyof typeof templateRegistry
|
||||
|
||||
146
layers/templates/GrContents01/index.vue
Normal file
146
layers/templates/GrContents01/index.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<script setup lang="ts">
|
||||
import { SplideSlide } from '@splidejs/vue-splide'
|
||||
import {
|
||||
getComponentContainer,
|
||||
getComponentGroup,
|
||||
} from '#layers/utils/dataUtil'
|
||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
||||
|
||||
interface Props {
|
||||
components: PageDataTemplateComponents
|
||||
pageVerTmplSeq: number
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
|
||||
const backgroundData = computed(() =>
|
||||
getComponentGroup(props.components, 'background')
|
||||
)
|
||||
const mainTitleData = computed(() =>
|
||||
getComponentGroup(props.components, 'mainTitle')
|
||||
)
|
||||
const subTitleData = computed(() =>
|
||||
getComponentGroup(props.components, 'subTitle')
|
||||
)
|
||||
const descriptionData = computed(() =>
|
||||
getComponentGroup(props.components, 'description')
|
||||
)
|
||||
const slideData = computed(() => {
|
||||
return getComponentContainer(props.components, 'group_sets')
|
||||
})
|
||||
const buttonListData = computed(() => {
|
||||
return getComponentGroupAry(props.components, 'buttonList')
|
||||
})
|
||||
const paginationData = computed(() => {
|
||||
return getComponentGroupAry(props.components, 'pagination')
|
||||
})
|
||||
|
||||
const onArrowClick = (direction, targetIndex) => {
|
||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="relative py-[80px] md:py-[120px]">
|
||||
<WidgetsBackground v-if="backgroundData" :resources-data="backgroundData" />
|
||||
<div class="section-content px-0">
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="title-xlg mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
<WidgetsSubTitle
|
||||
v-if="subTitleData"
|
||||
:resources-data="subTitleData"
|
||||
class="title-sm mt-2 mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
<template v-if="slideData">
|
||||
<div v-if="slideData.length <= 2" class="img-container">
|
||||
<div
|
||||
v-for="(item, index) in slideData"
|
||||
:key="index"
|
||||
:class="[{ 'slide-2': slideData.length === 2 }]"
|
||||
>
|
||||
<AtomsImg
|
||||
:resources-data="getComponentGroup(item, 'imgList')"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
:alt="getComponentGroup(item, 'subTitle')?.display?.text"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<BlocksSlideDefault
|
||||
v-else
|
||||
:type="slideData.length === 3 ? 'slide' : 'loop'"
|
||||
:slide-item-length="slideData?.length"
|
||||
:arrows="slideData.length === 3 ? false : true"
|
||||
:pagination="slideData.length === 3 ? false : true"
|
||||
:pagination-data="paginationData"
|
||||
:breakpoints="{
|
||||
1023: {
|
||||
type: 'loop',
|
||||
pagination: false,
|
||||
},
|
||||
}"
|
||||
class="mt-[32px]"
|
||||
@arrow-click="onArrowClick"
|
||||
>
|
||||
<SplideSlide
|
||||
v-for="(item, index) in slideData"
|
||||
:key="index"
|
||||
class="mr-4"
|
||||
>
|
||||
<div class="slide-inner w-[295px] sm:w-[304px]">
|
||||
<AtomsImg
|
||||
:resources-data="getComponentGroup(item, 'imgList')"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
:alt="getComponentGroup(item, 'subTitle')?.display?.text"
|
||||
/>
|
||||
</div>
|
||||
</SplideSlide>
|
||||
</BlocksSlideDefault>
|
||||
</template>
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
class="mt-[56px] mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="descriptionData"
|
||||
:resources-data="descriptionData"
|
||||
class="mt-8 mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.img-container {
|
||||
@apply flex flex-wrap justify-center gap-4 box-content mx-auto mt-[32px]
|
||||
max-w-[688px] px-[20px]
|
||||
md:max-w-[944px] md:px-[40px];
|
||||
}
|
||||
.splide {
|
||||
@apply md:max-w-[1088px];
|
||||
}
|
||||
.splide:deep(.splide__track) {
|
||||
@apply md:w-[944px] md:mx-auto;
|
||||
}
|
||||
.splide:deep(.splide__track) {
|
||||
@apply !px-[20px] sm:!px-[40px] md:!px-[0];
|
||||
}
|
||||
.splide:deep(.arrow-next) {
|
||||
@apply md:-right-[0];
|
||||
}
|
||||
.splide:deep(.arrow-prev) {
|
||||
@apply md:-left-[0];
|
||||
}
|
||||
.slide-2 {
|
||||
@apply max-w-[335px] md:max-w-[464px];
|
||||
}
|
||||
</style>
|
||||
@@ -62,12 +62,12 @@ const handleSplideMove = (_splide: SplideType, newIndex: number) => {
|
||||
<WidgetsMainTitle
|
||||
v-if="hasComponentGroup(item, 'mainTitle')"
|
||||
:resources-data="getComponentGroup(item, 'mainTitle')"
|
||||
class="title-md"
|
||||
class="title-lg"
|
||||
/>
|
||||
<WidgetsSubTitle
|
||||
v-if="hasComponentGroup(item, 'subTitle')"
|
||||
:resources-data="getComponentGroup(item, 'subTitle')"
|
||||
class="title-sm mt-0.5 line-clamp-3 md:mt-1 md:line-clamp-2"
|
||||
class="title-md mt-0.5 line-clamp-3 md:mt-1 md:line-clamp-2"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="hasComponentGroup(item, 'description')"
|
||||
|
||||
@@ -36,12 +36,12 @@ const paginationData = computed(() => {
|
||||
<WidgetsMainTitle
|
||||
v-if="hasComponentGroup(item, 'mainTitle')"
|
||||
:resources-data="getComponentGroup(item, 'mainTitle')"
|
||||
class="title-md"
|
||||
class="title-lg"
|
||||
/>
|
||||
<WidgetsSubTitle
|
||||
v-if="hasComponentGroup(item, 'subTitle')"
|
||||
:resources-data="getComponentGroup(item, 'subTitle')"
|
||||
class="title-sm mt-0.5 line-clamp-3 md:mt-1 md:line-clamp-2"
|
||||
class="title-md mt-0.5 line-clamp-3 md:mt-1 md:line-clamp-2"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="hasComponentGroup(item, 'description')"
|
||||
|
||||
@@ -70,13 +70,13 @@ const handleSplideMove = (_splide: SplideType, newIndex: number) => {
|
||||
<WidgetsMainTitle
|
||||
v-if="hasComponentGroup(item, 'mainTitle')"
|
||||
:resources-data="getComponentGroup(item, 'mainTitle')"
|
||||
class="title-md line-clamp-1 text-left"
|
||||
class="title-lg line-clamp-1 text-left"
|
||||
/>
|
||||
<WidgetsSubTitle
|
||||
v-if="hasComponentGroup(item, 'subTitle')"
|
||||
:resources-data="getComponentGroup(item, 'subTitle')"
|
||||
tag="p"
|
||||
class="title-sm mt-1 line-clamp-1 text-left"
|
||||
class="title-md mt-1 line-clamp-1 text-left"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="hasComponentGroup(item, 'description')"
|
||||
|
||||
@@ -108,7 +108,7 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="title-sm"
|
||||
class="title-md"
|
||||
/>
|
||||
<BlocksSlideThumbnail
|
||||
ref="slideThumbnailRef"
|
||||
|
||||
@@ -65,7 +65,7 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="title-sm"
|
||||
class="title-md mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
<BlocksSlideCenterFocus
|
||||
v-if="slideData"
|
||||
|
||||
@@ -76,7 +76,7 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="title-sm"
|
||||
class="title-md mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
<BlocksSlideCenterHighlight
|
||||
v-if="slideData"
|
||||
@@ -100,18 +100,18 @@ const onArrowClick = (direction, targetIndex) => {
|
||||
<WidgetsSubTitle
|
||||
v-if="imgTitleData"
|
||||
:resources-data="imgTitleData"
|
||||
class="title-md mt-[32px] line-clamp-2 md:line-clamp-1"
|
||||
class="title-lg mt-[32px] mx-[20px] line-clamp-2 sm:mx-[40px] md:line-clamp-1"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="descriptionData"
|
||||
:resources-data="descriptionData"
|
||||
class="mt-[8px] md:mt-[16px]"
|
||||
class="mt-[8px] mx-[20px] sm:mx-[40px] md:mt-[16px]"
|
||||
/>
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
||||
class="mt-[32px]"
|
||||
class="mt-[32px] mx-[20px] sm:mx-[40px]"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -47,7 +47,7 @@ const { data: slideData } = await useAsyncData(
|
||||
pageSeq: pageData.value.page_seq,
|
||||
pageVer: pageData.value.page_ver,
|
||||
pageVerTmplSeq: props.pageVerTmplSeq,
|
||||
langCode: 'ko',
|
||||
langCode: locale.value,
|
||||
})
|
||||
|
||||
const bannerList = getComponentContainer(operateGroupList, 'bannerList', {
|
||||
@@ -94,12 +94,12 @@ const onArrowClick = direction => {
|
||||
<WidgetsMainTitle
|
||||
v-if="mainTitleData"
|
||||
:resources-data="mainTitleData"
|
||||
class="w-full max-w-[355px] md:max-w-[944px]"
|
||||
class="w-full max-w-[355px] mx-[20px] sm:mx-[40px] md:max-w-[944px]"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="descriptionData"
|
||||
:resources-data="descriptionData"
|
||||
class="w-full max-w-[355px] md:max-w-[944px]"
|
||||
class="w-full max-w-[355px] mx-[20px] sm:mx-[40px] md:max-w-[944px]"
|
||||
/>
|
||||
<WidgetsVideoPlay
|
||||
v-if="videoPlayData"
|
||||
|
||||
@@ -52,12 +52,12 @@ const onArrowClick = direction => {
|
||||
<WidgetsSubTitle
|
||||
v-if="hasComponentGroup(item, 'subTitle')"
|
||||
:resources-data="getComponentGroup(item, 'subTitle')"
|
||||
class="title-sm"
|
||||
class="title-md"
|
||||
/>
|
||||
<WidgetsMainTitle
|
||||
v-if="hasComponentGroup(item, 'mainTitle')"
|
||||
:resources-data="getComponentGroup(item, 'mainTitle')"
|
||||
class="title-lg"
|
||||
class="title-xlg"
|
||||
/>
|
||||
<WidgetsDescription
|
||||
v-if="hasComponentGroup(item, 'description')"
|
||||
|
||||
BIN
public/images/common/publisning_template_loader_black.png
Normal file
BIN
public/images/common/publisning_template_loader_black.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 266 KiB |
BIN
public/images/common/publisning_template_loader_white.png
Normal file
BIN
public/images/common/publisning_template_loader_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 257 KiB |
Reference in New Issue
Block a user