feat. 일반 템플릿 로그 추가
This commit is contained in:
@@ -18,27 +18,25 @@
|
|||||||
background-color: var(--pagination-active);
|
background-color: var(--pagination-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
.splide-arrows {
|
.splide__arrows {
|
||||||
@apply hidden md:block;
|
@apply hidden md:block;
|
||||||
}
|
}
|
||||||
.splide-arrow {
|
.splide__arrows .splide-arrow.btn-circle {
|
||||||
@apply absolute top-1/2 w-[40px] h-[40px] bg-cover bg-center bg-no-repeat -translate-y-1/2 cursor-pointer z-[5]
|
@apply absolute top-1/2 w-[40px] h-[40px] bg-cover bg-center bg-no-repeat -translate-y-1/2 cursor-pointer z-[5]
|
||||||
md:w-[48px] md:h-[48px]
|
md:w-[48px] md:h-[48px];
|
||||||
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:rounded-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
|
|
||||||
hover:after:opacity-10;
|
|
||||||
}
|
}
|
||||||
.arrow-prev {
|
.splide__arrow--prev {
|
||||||
@apply bg-[image:var(--arrow-prev)];
|
@apply bg-[image:var(--arrow-prev)];
|
||||||
}
|
}
|
||||||
.arrow-next {
|
.splide__arrow--next {
|
||||||
@apply bg-[image:var(--arrow-next)];
|
@apply bg-[image:var(--arrow-next)];
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-full .arrow-prev {
|
.type-full .splide__arrow--prev {
|
||||||
@apply left-10;
|
@apply left-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-full .arrow-next {
|
.type-full .splide__arrow--next {
|
||||||
@apply right-10;
|
@apply right-10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const emit = defineEmits<{
|
import type { TrackingObject } from '#layers/types/api/common'
|
||||||
(e: 'click'): void
|
|
||||||
}>()
|
interface Props {
|
||||||
|
tracking: TrackingObject
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const { locale } = useI18n()
|
||||||
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
|
|
||||||
|
const handlePlayClick = () => {
|
||||||
|
sendLog(locale.value, useAnalyticsData(props.tracking))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button class="btn-play" @click="emit('click')">
|
<button class="btn-play" @click="handlePlayClick">
|
||||||
<span class="sr-only">Play</span>
|
<span class="sr-only">Play</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ interface Props {
|
|||||||
title: string
|
title: string
|
||||||
description: string | number
|
description: string | number
|
||||||
imgPath: string | null
|
imgPath: string | null
|
||||||
|
analyticsSarea: string
|
||||||
linkTarget?: '_blank' | '_self'
|
linkTarget?: '_blank' | '_self'
|
||||||
url?: string
|
url?: string
|
||||||
alt?: string
|
alt?: string
|
||||||
@@ -10,8 +11,9 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
|
|
||||||
const isNoImage = computed(() => {
|
const isNoImage = computed(() => {
|
||||||
return !props.imgPath || props.imgPath === null
|
return !props.imgPath || props.imgPath === null
|
||||||
@@ -21,14 +23,12 @@ const isShowOverlay = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const handleLinkClick = (title: string) => {
|
const handleLinkClick = (title: string) => {
|
||||||
const trackingData = {
|
const analytics = {
|
||||||
tracking: {
|
|
||||||
click_item: title,
|
click_item: title,
|
||||||
action_type: 'click',
|
action_type: 'click',
|
||||||
click_sarea: '',
|
click_sarea: props.analyticsSarea,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(trackingData, 1))
|
sendLog(locale.value, useAnalyticsData(analytics))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
37
layers/components/blocks/slide/Arrows.vue
Normal file
37
layers/components/blocks/slide/Arrows.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
arrowsData?: PageDataResourceGroups
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const { locale } = useI18n()
|
||||||
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
|
|
||||||
|
const handleArrowClick = (direction: 'prev' | 'next') => {
|
||||||
|
if (props.arrowsData) {
|
||||||
|
const arrowIndex = direction === 'prev' ? 0 : 1
|
||||||
|
sendLog(
|
||||||
|
locale.value,
|
||||||
|
useAnalyticsData(props.arrowsData[arrowIndex]?.tracking)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="splide__arrows">
|
||||||
|
<AtomsButtonCircle
|
||||||
|
sr-only="Previous"
|
||||||
|
class="splide-arrow splide__arrow--prev"
|
||||||
|
@click="handleArrowClick('prev')"
|
||||||
|
/>
|
||||||
|
<AtomsButtonCircle
|
||||||
|
sr-only="Next"
|
||||||
|
class="splide-arrow splide__arrow--next"
|
||||||
|
@click="handleArrowClick('next')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -9,15 +9,16 @@ import type {
|
|||||||
} from '#layers/types/api/gameData'
|
} from '#layers/types/api/gameData'
|
||||||
|
|
||||||
const MORE_WIDTH = 72
|
const MORE_WIDTH = 72
|
||||||
const START_WIDTH_MARGIN = 40
|
const START_MARGIN = 40
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { tm } = useI18n()
|
const { locale, tm } = useI18n()
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
const gameDataStore = useGameDataStore()
|
const gameDataStore = useGameDataStore()
|
||||||
const pageDataStore = usePageDataStore()
|
const pageDataStore = usePageDataStore()
|
||||||
const scrollStore = useScrollStore()
|
const scrollStore = useScrollStore()
|
||||||
const breakpoints = useResponsiveBreakpoints()
|
const breakpoints = useResponsiveBreakpoints()
|
||||||
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
|
|
||||||
const { gameData } = storeToRefs(gameDataStore)
|
const { gameData } = storeToRefs(gameDataStore)
|
||||||
const { pageLayoutType } = storeToRefs(pageDataStore)
|
const { pageLayoutType } = storeToRefs(pageDataStore)
|
||||||
@@ -95,7 +96,7 @@ const calculateOverflow = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const screenWidth = width.value
|
const screenWidth = width.value
|
||||||
const totalNavWidth = navWidth.value + startWidth.value + START_WIDTH_MARGIN
|
const totalNavWidth = navWidth.value + startWidth.value + START_MARGIN
|
||||||
|
|
||||||
// 해상도가 navWidth + startWidth보다 작은 경우
|
// 해상도가 navWidth + startWidth보다 작은 경우
|
||||||
if (screenWidth < totalNavWidth) {
|
if (screenWidth < totalNavWidth) {
|
||||||
@@ -120,18 +121,6 @@ const calculateOverflow = () => {
|
|||||||
// 100ms마다 최대 1회 실행
|
// 100ms마다 최대 1회 실행
|
||||||
const throttledCalculateOverflow = useThrottleFn(calculateOverflow, 100)
|
const throttledCalculateOverflow = useThrottleFn(calculateOverflow, 100)
|
||||||
|
|
||||||
const handleMenuOpen = () => {
|
|
||||||
isMenuOpen.value = true
|
|
||||||
scrollStore.controlScrollLock(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMenuClose = (isPassing: boolean = false) => {
|
|
||||||
if (isPassing) return
|
|
||||||
|
|
||||||
isMenuOpen.value = false
|
|
||||||
scrollStore.controlScrollLock(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNotClickable = (gnbItem: GameDataMenu) => {
|
const isNotClickable = (gnbItem: GameDataMenu) => {
|
||||||
return gnbItem.click_action_type === 0
|
return gnbItem.click_action_type === 0
|
||||||
}
|
}
|
||||||
@@ -140,6 +129,32 @@ const has2depthButton = (gnbItem: GameDataMenu) => {
|
|||||||
return gnbItem.children && Object.keys(gnbItem.children).length > 0
|
return gnbItem.children && Object.keys(gnbItem.children).length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMenuOpen = () => {
|
||||||
|
isMenuOpen.value = true
|
||||||
|
scrollStore.controlScrollLock(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMenuClose = () => {
|
||||||
|
isMenuOpen.value = false
|
||||||
|
scrollStore.controlScrollLock(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSendLog = (item: string) => {
|
||||||
|
const analytics = {
|
||||||
|
action_type: 'click',
|
||||||
|
click_item: item,
|
||||||
|
click_sarea: 'GNB',
|
||||||
|
}
|
||||||
|
sendLog(locale.value, useAnalyticsData(analytics))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleGnbItemClick = (gnbItem: GameDataMenu) => {
|
||||||
|
if (isNotClickable(gnbItem)) return
|
||||||
|
|
||||||
|
handleMenuClose()
|
||||||
|
sendLog(locale.value, useAnalyticsData(gnbItem.tracking_json))
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
overflowCount.value = 0
|
overflowCount.value = 0
|
||||||
isMounted.value = true
|
isMounted.value = true
|
||||||
@@ -170,7 +185,11 @@ onMounted(() => {
|
|||||||
v-if="gnbData"
|
v-if="gnbData"
|
||||||
:class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]"
|
:class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]"
|
||||||
>
|
>
|
||||||
<AtomsLocaleLink to="/home" class="mx-auto md:hidden">
|
<AtomsLocaleLink
|
||||||
|
to="/home"
|
||||||
|
class="mx-auto md:hidden"
|
||||||
|
@click="handleSendLog('BI')"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
:src="formatPathHost(gnbData?.bi_path)"
|
:src="formatPathHost(gnbData?.bi_path)"
|
||||||
:alt="gameData?.game_name"
|
:alt="gameData?.game_name"
|
||||||
@@ -183,11 +202,14 @@ onMounted(() => {
|
|||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
:class="['nav-wrap', { 'is-open': isMenuOpen }]"
|
:class="['nav-wrap', { 'is-open': isMenuOpen }]"
|
||||||
@click="handleMenuClose()"
|
@click="handleMenuClose"
|
||||||
>
|
>
|
||||||
<div ref="navAreaRef" class="nav-area" @click.stop>
|
<div ref="navAreaRef" class="nav-area" @click.stop>
|
||||||
<div class="nav-logo">
|
<div class="nav-logo">
|
||||||
<AtomsLocaleLink to="/home" @click="handleMenuClose">
|
<AtomsLocaleLink
|
||||||
|
to="/home"
|
||||||
|
@click="[handleMenuClose(), handleSendLog('BI')]"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
:src="formatPathHost(gnbData?.bi_path)"
|
:src="formatPathHost(gnbData?.bi_path)"
|
||||||
:alt="gameData?.game_name"
|
:alt="gameData?.game_name"
|
||||||
@@ -219,7 +241,7 @@ onMounted(() => {
|
|||||||
'router-link-active': hasActiveChild(gnbItem.children),
|
'router-link-active': hasActiveChild(gnbItem.children),
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
@click="handleMenuClose(isNotClickable(gnbItem))"
|
@click="handleGnbItemClick(gnbItem)"
|
||||||
>
|
>
|
||||||
<span>{{ gnbItem.menu_name }}</span>
|
<span>{{ gnbItem.menu_name }}</span>
|
||||||
<AtomsIconsWebLinkLine
|
<AtomsIconsWebLinkLine
|
||||||
@@ -248,7 +270,7 @@ onMounted(() => {
|
|||||||
:to="child.url_path"
|
:to="child.url_path"
|
||||||
:target="child.link_target"
|
:target="child.link_target"
|
||||||
class="item-link"
|
class="item-link"
|
||||||
@click="handleMenuClose(isNotClickable(child))"
|
@click="handleGnbItemClick(child)"
|
||||||
>
|
>
|
||||||
<span>{{ child.menu_name }}</span>
|
<span>{{ child.menu_name }}</span>
|
||||||
<AtomsIconsWebLinkLine
|
<AtomsIconsWebLinkLine
|
||||||
@@ -293,7 +315,7 @@ onMounted(() => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
@click="handleMenuClose(isNotClickable(gnbItem))"
|
@click="handleGnbItemClick(gnbItem)"
|
||||||
>
|
>
|
||||||
<span>{{ gnbItem.menu_name }}</span>
|
<span>{{ gnbItem.menu_name }}</span>
|
||||||
</component>
|
</component>
|
||||||
@@ -340,7 +362,7 @@ onMounted(() => {
|
|||||||
'nav-1depth',
|
'nav-1depth',
|
||||||
{ 'router-link-active': pageLayoutType === 'promotion' },
|
{ 'router-link-active': pageLayoutType === 'promotion' },
|
||||||
]"
|
]"
|
||||||
@click="handleMenuClose"
|
@click="[handleMenuClose(), handleSendLog('이벤트')]"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="flex items-center gap-1 flex-1 text-gradient-pink"
|
class="flex items-center gap-1 flex-1 text-gradient-pink"
|
||||||
@@ -367,6 +389,9 @@ onMounted(() => {
|
|||||||
:text-color="
|
:text-color="
|
||||||
getColorCodeFromData(start1depthData?.btn_info, 'txt')
|
getColorCodeFromData(start1depthData?.btn_info, 'txt')
|
||||||
"
|
"
|
||||||
|
@click="
|
||||||
|
sendLog(locale, useAnalyticsData(start1depthData.tracking))
|
||||||
|
"
|
||||||
>
|
>
|
||||||
{{ start1depthData?.btn_info?.txt_btn_name }}
|
{{ start1depthData?.btn_info?.txt_btn_name }}
|
||||||
</BlocksButtonLauncher>
|
</BlocksButtonLauncher>
|
||||||
@@ -376,7 +401,13 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(item, key) in start2depthData" :key="key">
|
<li v-for="(item, key) in start2depthData" :key="key">
|
||||||
<BlocksButtonLauncher type="custom" :platform="key">
|
<BlocksButtonLauncher
|
||||||
|
type="custom"
|
||||||
|
:platform="key"
|
||||||
|
@click="
|
||||||
|
sendLog(locale, useAnalyticsData(item.tracking))
|
||||||
|
"
|
||||||
|
>
|
||||||
{{ item.btn_info?.txt_btn_name }}
|
{{ item.btn_info?.txt_btn_name }}
|
||||||
</BlocksButtonLauncher>
|
</BlocksButtonLauncher>
|
||||||
</li>
|
</li>
|
||||||
@@ -385,7 +416,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
<button class="btn-close" @click="handleMenuClose()">
|
<button class="btn-close" @click="handleMenuClose">
|
||||||
<AtomsIconsCloseLine
|
<AtomsIconsCloseLine
|
||||||
size="24"
|
size="24"
|
||||||
color="var(--foreground-reversal)"
|
color="var(--foreground-reversal)"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ watch(isPAssApiLoading, newVal => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { sendLog } = useAnalytics()
|
const { sendLog } = useAnalytics()
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect('view', 1))
|
sendLog(locale.value, useAnalyticsData('view'))
|
||||||
|
|
||||||
if (!hasApiCallStarted.value) {
|
if (!hasApiCallStarted.value) {
|
||||||
loadingStore.stopFullLoading()
|
loadingStore.stopFullLoading()
|
||||||
@@ -94,6 +94,7 @@ onMounted(() => {
|
|||||||
:id="template.page_ver_tmpl_name_en"
|
:id="template.page_ver_tmpl_name_en"
|
||||||
:components="template.page_ver_tmpl_json"
|
:components="template.page_ver_tmpl_json"
|
||||||
:page-ver-tmpl-seq="template.page_ver_tmpl_seq"
|
:page-ver-tmpl-seq="template.page_ver_tmpl_seq"
|
||||||
|
:page-ver-tmpl-name-en="template.page_ver_tmpl_name_en"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import type { ButtonType } from '#layers/types/components/button'
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
resourcesData: PageDataResourceGroup[]
|
resourcesData: PageDataResourceGroup[]
|
||||||
pageVerTmplSeq: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -16,7 +15,7 @@ const { locale } = useI18n()
|
|||||||
const modalStore = useModalStore()
|
const modalStore = useModalStore()
|
||||||
const scrollStore = useScrollStore()
|
const scrollStore = useScrollStore()
|
||||||
const breakpoints = useResponsiveBreakpoints()
|
const breakpoints = useResponsiveBreakpoints()
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
const { tm } = useI18n()
|
const { tm } = useI18n()
|
||||||
const device = useDevice()
|
const device = useDevice()
|
||||||
|
|
||||||
@@ -92,8 +91,7 @@ const downloadFile = async (url: string = '', osType: number = 0) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleButtonClick = (button: PageDataResourceGroup) => {
|
const handleButtonClick = (button: PageDataResourceGroup) => {
|
||||||
// 로그
|
sendLog(locale.value, useAnalyticsData(button.tracking))
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(button, props.pageVerTmplSeq))
|
|
||||||
|
|
||||||
const btnDetail = button.btn_info?.detail
|
const btnDetail = button.btn_info?.detail
|
||||||
|
|
||||||
|
|||||||
@@ -3,27 +3,22 @@ import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
resourcesData: PageDataResourceGroup
|
resourcesData: PageDataResourceGroup
|
||||||
pageVerTmplSeq: number
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const modalStore = useModalStore()
|
const modalStore = useModalStore()
|
||||||
const { locale } = useI18n()
|
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
|
|
||||||
// 비디오 플레이 버튼 클릭 핸들러
|
// 비디오 플레이 버튼 클릭 핸들러
|
||||||
const handleVideoPlayClick = () => {
|
const handleVideoPlayClick = () => {
|
||||||
const youtubeUrl = props.resourcesData?.display?.text ?? ''
|
const youtubeUrl = props.resourcesData?.display?.text ?? ''
|
||||||
if (youtubeUrl) {
|
if (youtubeUrl) {
|
||||||
modalStore.handleOpenYoutube({ youtubeUrl })
|
modalStore.handleOpenYoutube({ youtubeUrl })
|
||||||
|
|
||||||
sendLog(
|
|
||||||
locale.value,
|
|
||||||
useAnalyticsLogDataDirect(props.resourcesData, props.pageVerTmplSeq)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AtomsButtonPlay @click="handleVideoPlayClick" />
|
<AtomsButtonPlay
|
||||||
|
:tracking="props.resourcesData.tracking"
|
||||||
|
@click="handleVideoPlayClick"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Splide } from '@splidejs/vue-splide'
|
import { Splide, SplideTrack } from '@splidejs/vue-splide'
|
||||||
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||||
import type { SlideItemSize } from '#layers/types/components/slide'
|
import type { SlideItemSize } from '#layers/types/components/slide'
|
||||||
import { useSplideArrow } from '#layers/composables/useSplideArrow'
|
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
slideItemSize: SlideItemSize
|
slideItemSize: SlideItemSize
|
||||||
@@ -10,6 +10,7 @@ interface Props {
|
|||||||
autoplay?: boolean
|
autoplay?: boolean
|
||||||
interval?: number
|
interval?: number
|
||||||
arrows?: boolean
|
arrows?: boolean
|
||||||
|
arrowsData?: PageDataResourceGroups
|
||||||
pagination?: boolean
|
pagination?: boolean
|
||||||
class?: string
|
class?: string
|
||||||
}
|
}
|
||||||
@@ -21,13 +22,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
pagination: true,
|
pagination: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
const emit = defineEmits(['mounted', 'move'])
|
||||||
|
|
||||||
const splideIndex = defineModel<number>('index', { required: false })
|
const splideIndex = defineModel<number>('index', { required: false })
|
||||||
|
|
||||||
// Splide 화살표 로직을 위한 composable 사용
|
|
||||||
const { addArrowClickListeners } = useSplideArrow()
|
|
||||||
|
|
||||||
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
||||||
|
|
||||||
const options = computed((): ResponsiveOptions => {
|
const options = computed((): ResponsiveOptions => {
|
||||||
@@ -45,10 +42,6 @@ const options = computed((): ResponsiveOptions => {
|
|||||||
interval: props.interval,
|
interval: props.interval,
|
||||||
flickPower: 300,
|
flickPower: 300,
|
||||||
classes: {
|
classes: {
|
||||||
arrows: 'splide-arrows',
|
|
||||||
arrow: 'splide-arrow',
|
|
||||||
prev: 'arrow-prev',
|
|
||||||
next: 'arrow-next',
|
|
||||||
pagination: 'splide-pagination-bullets',
|
pagination: 'splide-pagination-bullets',
|
||||||
page: 'splide-pagination-bullet',
|
page: 'splide-pagination-bullet',
|
||||||
},
|
},
|
||||||
@@ -95,13 +88,6 @@ const handleSplideMounted = (splide: SplideType) => {
|
|||||||
if (splideIndex.value !== undefined) {
|
if (splideIndex.value !== undefined) {
|
||||||
splideIndex.value = splide.index
|
splideIndex.value = splide.index
|
||||||
}
|
}
|
||||||
|
|
||||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
|
||||||
nextTick(() => {
|
|
||||||
addArrowClickListeners(splide, (direction, targetIndex) => {
|
|
||||||
emit('arrowClick', direction, targetIndex)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMove = (
|
const handleMove = (
|
||||||
@@ -122,10 +108,14 @@ const handleMove = (
|
|||||||
<div :class="`center-focus ${props.class || ''}`" :style="style">
|
<div :class="`center-focus ${props.class || ''}`" :style="style">
|
||||||
<Splide
|
<Splide
|
||||||
:options="options"
|
:options="options"
|
||||||
|
:has-track="false"
|
||||||
@splide:mounted="handleSplideMounted"
|
@splide:mounted="handleSplideMounted"
|
||||||
@splide:move="handleMove"
|
@splide:move="handleMove"
|
||||||
>
|
>
|
||||||
|
<SplideTrack>
|
||||||
<slot />
|
<slot />
|
||||||
|
</SplideTrack>
|
||||||
|
<BlocksSlideArrows v-if="props.arrows" :arrows-data="props.arrowsData" />
|
||||||
</Splide>
|
</Splide>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -192,10 +182,10 @@ const handleMove = (
|
|||||||
width: var(--banner-width-pc-active);
|
width: var(--banner-width-pc-active);
|
||||||
height: var(--banner-height-pc-active);
|
height: var(--banner-height-pc-active);
|
||||||
}
|
}
|
||||||
.center-focus:deep(.arrow-prev) {
|
.center-focus:deep(.splide__arrow--prev) {
|
||||||
left: 28px;
|
left: 28px;
|
||||||
}
|
}
|
||||||
.center-focus:deep(.arrow-next) {
|
.center-focus:deep(.splide__arrow--next) {
|
||||||
right: 28px;
|
right: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Splide } from '@splidejs/vue-splide'
|
import { Splide, SplideTrack } from '@splidejs/vue-splide'
|
||||||
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||||
import type { SlideItemSize } from '#layers/types/components/slide'
|
import type { SlideItemSize } from '#layers/types/components/slide'
|
||||||
|
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
slideItemSize: SlideItemSize
|
slideItemSize: SlideItemSize
|
||||||
@@ -9,6 +10,7 @@ interface Props {
|
|||||||
autoplay?: boolean
|
autoplay?: boolean
|
||||||
interval?: number
|
interval?: number
|
||||||
arrows?: boolean
|
arrows?: boolean
|
||||||
|
arrowsData?: PageDataResourceGroups
|
||||||
pagination?: boolean
|
pagination?: boolean
|
||||||
class?: string
|
class?: string
|
||||||
}
|
}
|
||||||
@@ -20,13 +22,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
pagination: true,
|
pagination: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
const emit = defineEmits(['mounted', 'move'])
|
||||||
|
|
||||||
const splideIndex = defineModel<number>('index', { required: false })
|
const splideIndex = defineModel<number>('index', { required: false })
|
||||||
|
|
||||||
// Splide 화살표 로직을 위한 composable 사용
|
|
||||||
const { addArrowClickListeners } = useSplideArrow()
|
|
||||||
|
|
||||||
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
const isMultipleItems = computed(() => (props.slideItemLength ?? 0) > 1)
|
||||||
|
|
||||||
const options = computed((): ResponsiveOptions => {
|
const options = computed((): ResponsiveOptions => {
|
||||||
@@ -44,10 +42,6 @@ const options = computed((): ResponsiveOptions => {
|
|||||||
interval: props.interval,
|
interval: props.interval,
|
||||||
flickPower: 300,
|
flickPower: 300,
|
||||||
classes: {
|
classes: {
|
||||||
arrows: 'splide-arrows',
|
|
||||||
arrow: 'splide-arrow',
|
|
||||||
prev: 'arrow-prev',
|
|
||||||
next: 'arrow-next',
|
|
||||||
pagination: 'splide-pagination-bullets',
|
pagination: 'splide-pagination-bullets',
|
||||||
page: 'splide-pagination-bullet',
|
page: 'splide-pagination-bullet',
|
||||||
},
|
},
|
||||||
@@ -97,13 +91,6 @@ const handleSplideMounted = (splide: SplideType) => {
|
|||||||
if (splideIndex.value !== undefined) {
|
if (splideIndex.value !== undefined) {
|
||||||
splideIndex.value = splide.index
|
splideIndex.value = splide.index
|
||||||
}
|
}
|
||||||
|
|
||||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
|
||||||
nextTick(() => {
|
|
||||||
addArrowClickListeners(splide, (direction, targetIndex) => {
|
|
||||||
emit('arrowClick', direction, targetIndex)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMove = (
|
const handleMove = (
|
||||||
@@ -124,10 +111,14 @@ const handleMove = (
|
|||||||
<div :class="`center-highlight ${props.class || ''}`" :style="style">
|
<div :class="`center-highlight ${props.class || ''}`" :style="style">
|
||||||
<Splide
|
<Splide
|
||||||
:options="options"
|
:options="options"
|
||||||
|
:has-track="false"
|
||||||
@splide:mounted="handleSplideMounted"
|
@splide:mounted="handleSplideMounted"
|
||||||
@splide:move="handleMove"
|
@splide:move="handleMove"
|
||||||
>
|
>
|
||||||
|
<SplideTrack>
|
||||||
<slot />
|
<slot />
|
||||||
|
</SplideTrack>
|
||||||
|
<BlocksSlideArrows v-if="props.arrows" :arrows-data="props.arrowsData" />
|
||||||
</Splide>
|
</Splide>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -179,10 +170,10 @@ const handleMove = (
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
.center-highlight:deep(.arrow-prev) {
|
.center-highlight:deep(.splide__arrow--prev) {
|
||||||
margin-left: calc(-1 * var(--banner-arrow-pc));
|
margin-left: calc(-1 * var(--banner-arrow-pc));
|
||||||
}
|
}
|
||||||
.center-highlight:deep(.arrow-next) {
|
.center-highlight:deep(.splide__arrow--next) {
|
||||||
margin-left: var(--banner-arrow-pc);
|
margin-left: var(--banner-arrow-pc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Splide } from '@splidejs/vue-splide'
|
import { Splide, SplideTrack } from '@splidejs/vue-splide'
|
||||||
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||||
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ interface Props {
|
|||||||
perPage?: number
|
perPage?: number
|
||||||
drag?: boolean
|
drag?: boolean
|
||||||
arrows?: boolean
|
arrows?: boolean
|
||||||
|
arrowsData?: PageDataResourceGroups
|
||||||
pagination?: boolean
|
pagination?: boolean
|
||||||
paginationData?: PageDataResourceGroups
|
paginationData?: PageDataResourceGroups
|
||||||
destroy?: boolean
|
destroy?: boolean
|
||||||
@@ -29,13 +30,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
destroy: false,
|
destroy: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
const emit = defineEmits(['mounted', 'move'])
|
||||||
|
|
||||||
const splideIndex = defineModel<number>('index', { required: false })
|
const splideIndex = defineModel<number>('index', { required: false })
|
||||||
|
|
||||||
// Splide 화살표 로직을 위한 composable 사용
|
|
||||||
const { addArrowClickListeners } = useSplideArrow()
|
|
||||||
|
|
||||||
const slideType = computed(() => {
|
const slideType = computed(() => {
|
||||||
if (props.type) return props.type
|
if (props.type) return props.type
|
||||||
|
|
||||||
@@ -60,10 +58,6 @@ const options = computed((): ResponsiveOptions => {
|
|||||||
destroy: props.destroy,
|
destroy: props.destroy,
|
||||||
flickPower: 300,
|
flickPower: 300,
|
||||||
classes: {
|
classes: {
|
||||||
arrows: 'splide-arrows',
|
|
||||||
arrow: 'splide-arrow',
|
|
||||||
prev: 'arrow-prev',
|
|
||||||
next: 'arrow-next',
|
|
||||||
pagination: 'splide-pagination-bullets',
|
pagination: 'splide-pagination-bullets',
|
||||||
page: 'splide-pagination-bullet',
|
page: 'splide-pagination-bullet',
|
||||||
},
|
},
|
||||||
@@ -77,13 +71,6 @@ const handleSplideMounted = (splide: SplideType) => {
|
|||||||
if (splideIndex.value !== undefined) {
|
if (splideIndex.value !== undefined) {
|
||||||
splideIndex.value = splide.index
|
splideIndex.value = splide.index
|
||||||
}
|
}
|
||||||
|
|
||||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
|
||||||
nextTick(() => {
|
|
||||||
addArrowClickListeners(splide, (direction, targetIndex) => {
|
|
||||||
emit('arrowClick', direction, targetIndex)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMove = (
|
const handleMove = (
|
||||||
@@ -103,10 +90,14 @@ const handleMove = (
|
|||||||
<template>
|
<template>
|
||||||
<Splide
|
<Splide
|
||||||
:options="options"
|
:options="options"
|
||||||
|
:has-track="false"
|
||||||
:style="getPaginationClass(props?.paginationData)"
|
:style="getPaginationClass(props?.paginationData)"
|
||||||
@splide:mounted="handleSplideMounted"
|
@splide:mounted="handleSplideMounted"
|
||||||
@splide:move="handleMove"
|
@splide:move="handleMove"
|
||||||
>
|
>
|
||||||
|
<SplideTrack>
|
||||||
<slot />
|
<slot />
|
||||||
|
</SplideTrack>
|
||||||
|
<BlocksSlideArrows v-if="props.arrows" :arrows-data="props.arrowsData" />
|
||||||
</Splide>
|
</Splide>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Splide } from '@splidejs/vue-splide'
|
import { Splide, SplideTrack } from '@splidejs/vue-splide'
|
||||||
import { useSplideArrow } from '#layers/composables/useSplideArrow'
|
|
||||||
import { getPaginationClass } from '#layers/utils/styleUtil'
|
import { getPaginationClass } from '#layers/utils/styleUtil'
|
||||||
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||||
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
import type { PageDataResourceGroups } from '#layers/types/api/pageData'
|
||||||
@@ -10,6 +9,7 @@ interface Props {
|
|||||||
autoplay?: boolean
|
autoplay?: boolean
|
||||||
interval?: number
|
interval?: number
|
||||||
arrows?: boolean
|
arrows?: boolean
|
||||||
|
arrowsData?: PageDataResourceGroups
|
||||||
pagination?: boolean
|
pagination?: boolean
|
||||||
paginationData?: PageDataResourceGroups
|
paginationData?: PageDataResourceGroups
|
||||||
}
|
}
|
||||||
@@ -22,13 +22,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
pagination: true,
|
pagination: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
const emit = defineEmits(['mounted', 'move'])
|
||||||
|
|
||||||
const splideIndex = defineModel<number>('index', { required: false })
|
const splideIndex = defineModel<number>('index', { required: false })
|
||||||
|
|
||||||
const splideRef = ref()
|
const splideRef = ref()
|
||||||
// Splide 화살표 로직을 위한 composable 사용
|
|
||||||
const { addArrowClickListeners } = useSplideArrow()
|
|
||||||
|
|
||||||
const options = computed((): ResponsiveOptions => {
|
const options = computed((): ResponsiveOptions => {
|
||||||
return {
|
return {
|
||||||
@@ -48,10 +46,6 @@ const options = computed((): ResponsiveOptions => {
|
|||||||
pagination: props.pagination,
|
pagination: props.pagination,
|
||||||
flickPower: 300,
|
flickPower: 300,
|
||||||
classes: {
|
classes: {
|
||||||
arrows: 'splide-arrows type-full',
|
|
||||||
arrow: 'splide-arrow',
|
|
||||||
prev: 'arrow-prev',
|
|
||||||
next: 'arrow-next',
|
|
||||||
pagination: 'splide-pagination-bullets type-full',
|
pagination: 'splide-pagination-bullets type-full',
|
||||||
page: 'splide-pagination-bullet',
|
page: 'splide-pagination-bullet',
|
||||||
},
|
},
|
||||||
@@ -64,13 +58,6 @@ const handleSplideMounted = (splide: SplideType) => {
|
|||||||
if (splideIndex.value !== undefined) {
|
if (splideIndex.value !== undefined) {
|
||||||
splideIndex.value = splide.index
|
splideIndex.value = splide.index
|
||||||
}
|
}
|
||||||
|
|
||||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
|
||||||
nextTick(() => {
|
|
||||||
addArrowClickListeners(splide, (direction, targetIndex) => {
|
|
||||||
emit('arrowClick', direction, targetIndex)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMove = (
|
const handleMove = (
|
||||||
@@ -95,12 +82,16 @@ defineExpose({
|
|||||||
<Splide
|
<Splide
|
||||||
ref="splideRef"
|
ref="splideRef"
|
||||||
:options="options"
|
:options="options"
|
||||||
|
:has-track="false"
|
||||||
class="h-full"
|
class="h-full"
|
||||||
:style="getPaginationClass(props.paginationData)"
|
:style="getPaginationClass(props.paginationData)"
|
||||||
@splide:mounted="handleSplideMounted"
|
@splide:mounted="handleSplideMounted"
|
||||||
@splide:move="handleMove"
|
@splide:move="handleMove"
|
||||||
>
|
>
|
||||||
|
<SplideTrack>
|
||||||
<slot />
|
<slot />
|
||||||
|
</SplideTrack>
|
||||||
|
<BlocksSlideArrows v-if="props.arrows" :arrows-data="props.arrowsData" />
|
||||||
</Splide>
|
</Splide>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Splide, SplideSlide } from '@splidejs/vue-splide'
|
import { Splide, SplideTrack, SplideSlide } from '@splidejs/vue-splide'
|
||||||
import { useSplideArrow } from '#layers/composables/useSplideArrow'
|
|
||||||
import type { Splide as SplideType, Options } from '@splidejs/splide'
|
import type { Splide as SplideType, Options } from '@splidejs/splide'
|
||||||
import type {
|
import type {
|
||||||
PageDataResourceGroups,
|
PageDataResourceGroups,
|
||||||
@@ -8,10 +7,12 @@ import type {
|
|||||||
} from '#layers/types/api/pageData'
|
} from '#layers/types/api/pageData'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
drag?: boolean
|
|
||||||
thumbnailData: PageDataResourceGroup[]
|
thumbnailData: PageDataResourceGroup[]
|
||||||
paginationData?: PageDataResourceGroups
|
paginationData: PageDataResourceGroups
|
||||||
|
analyticsSarea: string
|
||||||
|
drag?: boolean
|
||||||
arrows?: boolean
|
arrows?: boolean
|
||||||
|
arrowsData?: PageDataResourceGroups
|
||||||
variant?: 'default' | 'media'
|
variant?: 'default' | 'media'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,13 +22,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
variant: 'default',
|
variant: 'default',
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['mounted', 'move', 'arrowClick'])
|
const emit = defineEmits(['mounted', 'move'])
|
||||||
|
|
||||||
|
const { locale } = useI18n()
|
||||||
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
|
|
||||||
const splideIndex = defineModel<number>('index', { required: false })
|
const splideIndex = defineModel<number>('index', { required: false })
|
||||||
|
|
||||||
// Splide 화살표 로직을 위한 composable 사용
|
|
||||||
const { addArrowClickListeners } = useSplideArrow()
|
|
||||||
|
|
||||||
let mainInst: SplideType | null = null
|
let mainInst: SplideType | null = null
|
||||||
let thumbsInst: SplideType | null = null
|
let thumbsInst: SplideType | null = null
|
||||||
let removeArrowListeners: (() => void) | null = null
|
let removeArrowListeners: (() => void) | null = null
|
||||||
@@ -62,12 +63,6 @@ const thumbOptions = computed<Options>(() => ({
|
|||||||
isNavigation: true,
|
isNavigation: true,
|
||||||
updateOnMove: true,
|
updateOnMove: true,
|
||||||
flickPower: 300,
|
flickPower: 300,
|
||||||
classes: {
|
|
||||||
arrows: 'splide-arrows',
|
|
||||||
arrow: 'splide-arrow',
|
|
||||||
prev: 'arrow-prev',
|
|
||||||
next: 'arrow-next',
|
|
||||||
},
|
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
[BREAKPOINTS.md - 1]: {
|
[BREAKPOINTS.md - 1]: {
|
||||||
padding: {
|
padding: {
|
||||||
@@ -113,21 +108,22 @@ const handleMove = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleThumbnailClick = (title: string) => {
|
||||||
|
const paginationAnalytics = {
|
||||||
|
action_type: 'click',
|
||||||
|
click_item: title,
|
||||||
|
click_sarea: props.analyticsSarea,
|
||||||
|
}
|
||||||
|
|
||||||
|
sendLog(locale.value, useAnalyticsData(paginationAnalytics))
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
mainInst = mainRef.value?.splide ?? null
|
mainInst = mainRef.value?.splide ?? null
|
||||||
thumbsInst = thumbsRef.value?.splide ?? null
|
thumbsInst = thumbsRef.value?.splide ?? null
|
||||||
|
|
||||||
if (mainInst && thumbsInst) {
|
if (mainInst && thumbsInst) {
|
||||||
mainInst.sync(thumbsInst)
|
mainInst.sync(thumbsInst)
|
||||||
// 썸네일 슬라이드의 화살표 버튼에 이벤트 리스너 추가
|
|
||||||
nextTick(() => {
|
|
||||||
removeArrowListeners = addArrowClickListeners(
|
|
||||||
thumbsInst,
|
|
||||||
(direction, targetIndex) => {
|
|
||||||
emit('arrowClick', direction, targetIndex)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -163,13 +159,17 @@ onBeforeUnmount(() => {
|
|||||||
v-if="props.thumbnailData.length > 1"
|
v-if="props.thumbnailData.length > 1"
|
||||||
ref="thumbsRef"
|
ref="thumbsRef"
|
||||||
:options="thumbOptions"
|
:options="thumbOptions"
|
||||||
|
:arrows-data="props.arrowsData"
|
||||||
|
:has-track="false"
|
||||||
class="thumbnail-splide"
|
class="thumbnail-splide"
|
||||||
:style="getPaginationClass(paginationData)"
|
:style="getPaginationClass(paginationData)"
|
||||||
>
|
>
|
||||||
|
<SplideTrack>
|
||||||
<SplideSlide
|
<SplideSlide
|
||||||
v-for="(item, index) in props.thumbnailData"
|
v-for="(item, index) in props.thumbnailData"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="thumbnail-slide"
|
class="thumbnail-slide"
|
||||||
|
@click="handleThumbnailClick(item?.group_label ?? `${index + 1}`)"
|
||||||
>
|
>
|
||||||
<AtomsImg
|
<AtomsImg
|
||||||
:src="getThumbnailSrc(item)"
|
:src="getThumbnailSrc(item)"
|
||||||
@@ -177,6 +177,8 @@ onBeforeUnmount(() => {
|
|||||||
class="slide-image"
|
class="slide-image"
|
||||||
/>
|
/>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
|
</SplideTrack>
|
||||||
|
<BlocksSlideArrows v-if="props.arrows" :arrows-data="props.arrowsData" />
|
||||||
</Splide>
|
</Splide>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -226,10 +228,10 @@ onBeforeUnmount(() => {
|
|||||||
.thumbnail-carousel.thumbnail-default .thumbnail-splide {
|
.thumbnail-carousel.thumbnail-default .thumbnail-splide {
|
||||||
@apply absolute bottom-[32px] left-1/2 -translate-x-1/2 max-w-[100%] md:bottom-[48px] md:max-w-[896px] md:px-[64px];
|
@apply absolute bottom-[32px] left-1/2 -translate-x-1/2 max-w-[100%] md:bottom-[48px] md:max-w-[896px] md:px-[64px];
|
||||||
}
|
}
|
||||||
.thumbnail-carousel.thumbnail-default:deep(.arrow-prev) {
|
.thumbnail-carousel.thumbnail-default:deep(.splide__arrow--prev) {
|
||||||
@apply left-0;
|
@apply left-0;
|
||||||
}
|
}
|
||||||
.thumbnail-carousel.thumbnail-default:deep(.arrow-next) {
|
.thumbnail-carousel.thumbnail-default:deep(.splide__arrow--next) {
|
||||||
@apply right-0;
|
@apply right-0;
|
||||||
}
|
}
|
||||||
.thumbnail-carousel.thumbnail-default .thumbnail-slide {
|
.thumbnail-carousel.thumbnail-default .thumbnail-slide {
|
||||||
@@ -253,10 +255,10 @@ onBeforeUnmount(() => {
|
|||||||
sm:max-w-[calc(100%+80px)] sm:mx-[-40px]
|
sm:max-w-[calc(100%+80px)] sm:mx-[-40px]
|
||||||
md:max-w-[100%] md:mt-[28px] md:mx-auto md:px-[64px];
|
md:max-w-[100%] md:mt-[28px] md:mx-auto md:px-[64px];
|
||||||
}
|
}
|
||||||
.thumbnail-carousel.thumbnail-media:deep(.arrow-prev) {
|
.thumbnail-carousel.thumbnail-media:deep(.splide__arrow--prev) {
|
||||||
@apply left-[0];
|
@apply left-[0];
|
||||||
}
|
}
|
||||||
.thumbnail-carousel.thumbnail-media:deep(.arrow-next) {
|
.thumbnail-carousel.thumbnail-media:deep(.splide__arrow--next) {
|
||||||
@apply right-[0];
|
@apply right-[0];
|
||||||
}
|
}
|
||||||
.thumbnail-carousel.thumbnail-media .thumbnail-slide {
|
.thumbnail-carousel.thumbnail-media .thumbnail-slide {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as amplitude from '@amplitude/analytics-browser'
|
import * as amplitude from '@amplitude/analytics-browser'
|
||||||
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
import type { TrackingObject } from '#layers/types/api/common'
|
||||||
import type { AnalyticsDetailType } from '#layers/types/AnalyticsType'
|
import type { AnalyticsDetailType } from '#layers/types/AnalyticsType'
|
||||||
import type {
|
import type {
|
||||||
IdentityInfo,
|
IdentityInfo,
|
||||||
@@ -11,63 +11,9 @@ declare const svcLog: any
|
|||||||
declare const twq: any
|
declare const twq: any
|
||||||
declare const ttq: any
|
declare const ttq: any
|
||||||
|
|
||||||
/**
|
// ============================================================================
|
||||||
* 페이지 데이터와 템플릿 정보를 기반으로 분석용 로그 데이터를 생성하는 composable (직접 객체 반환)
|
// 유틸 함수
|
||||||
* @param resourcesData 페이지 리소스 데이터
|
// ============================================================================
|
||||||
* @param pageVerTmplSeq 템플릿 시퀀스 번호
|
|
||||||
* @returns 분석용 로그 데이터 객체 (ref 없이)
|
|
||||||
*/
|
|
||||||
export const useAnalyticsLogDataDirect = (
|
|
||||||
resourcesData: PageDataResourceGroup | string,
|
|
||||||
pageVerTmplSeq: number | null
|
|
||||||
) => {
|
|
||||||
const store = usePageDataStore()
|
|
||||||
const pageData = store.pageData
|
|
||||||
|
|
||||||
if (!pageData) {
|
|
||||||
return {} as AnalyticsDetailType
|
|
||||||
}
|
|
||||||
|
|
||||||
// resourcesData가 문자열인 경우 (예: 'view')
|
|
||||||
if (typeof resourcesData === 'string') {
|
|
||||||
const logData = {
|
|
||||||
actionType: resourcesData,
|
|
||||||
event: pageData.page_name,
|
|
||||||
eventCategory: pageData.page_name,
|
|
||||||
} as unknown as AnalyticsDetailType
|
|
||||||
|
|
||||||
if (resourcesData === 'view') {
|
|
||||||
logData.viewArea = pageData.page_name_en
|
|
||||||
logData.viewType = 'pageView'
|
|
||||||
}
|
|
||||||
|
|
||||||
return logData
|
|
||||||
}
|
|
||||||
|
|
||||||
// resourcesData가 객체인 경우 (기존 로직)
|
|
||||||
if (!resourcesData || !resourcesData.tracking) {
|
|
||||||
return {} as AnalyticsDetailType
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageDataTrack = resourcesData.tracking
|
|
||||||
|
|
||||||
const logData = {
|
|
||||||
actionType: pageDataTrack?.action_type,
|
|
||||||
event: pageData.page_name,
|
|
||||||
eventCategory: `${pageData.page_name}_${pageDataTrack?.click_sarea}_${pageDataTrack?.click_item}`,
|
|
||||||
} as unknown as AnalyticsDetailType
|
|
||||||
|
|
||||||
if (pageDataTrack.action_type === 'click') {
|
|
||||||
logData.clickArea = pageData.page_name_en
|
|
||||||
logData.clickSarea = pageDataTrack.click_sarea
|
|
||||||
logData.clickItem = pageDataTrack.click_item
|
|
||||||
} else if (pageDataTrack.action_type === 'view') {
|
|
||||||
logData.viewArea = pageData.page_name_en
|
|
||||||
logData.viewType = 'view_frame'
|
|
||||||
}
|
|
||||||
|
|
||||||
return logData
|
|
||||||
}
|
|
||||||
|
|
||||||
// target에 {XX1, XX2}와 같은 형태가 포함되어 있을 경우 options.clickItem으로부터 값 추출하여 세팅
|
// target에 {XX1, XX2}와 같은 형태가 포함되어 있을 경우 options.clickItem으로부터 값 추출하여 세팅
|
||||||
const findValueFromOption = (target: string, { options = {} }: any) => {
|
const findValueFromOption = (target: string, { options = {} }: any) => {
|
||||||
@@ -92,69 +38,112 @@ const findValueFromOption = (target: string, { options = {} }: any) => {
|
|||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 브라우저 환경인지 체크 */
|
||||||
|
const isClient = () => typeof window !== 'undefined' && import.meta.client
|
||||||
|
|
||||||
|
/** Analytics 객체가 비어있는지 체크 */
|
||||||
|
const isEmptyAnalytics = (analytics?: Partial<AnalyticsDetailType>) =>
|
||||||
|
!analytics || Object.keys(analytics).length === 0
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Analytics 데이터 생성
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Google Analytics 전송
|
* 로그 데이터를 생성하는 composable (직접 객체 반환)
|
||||||
*
|
* @param analytics 트래킹 데이터
|
||||||
* @param {AnalyticsDetailType} analytics
|
* @param type 'page' | 'template'
|
||||||
* @param {object} options
|
* @returns 분석용 로그 데이터 객체 (ref 없이)
|
||||||
*/
|
*/
|
||||||
const sendGA = (analytics: AnalyticsDetailType, { options = {} }: any) => {
|
export const useAnalyticsData = (
|
||||||
if (!import.meta.client) return
|
analytics: string | TrackingObject,
|
||||||
|
type: 'page' | 'template' = 'template'
|
||||||
|
): Partial<AnalyticsDetailType> => {
|
||||||
|
const pageDataStore = usePageDataStore()
|
||||||
|
const { pageName, pageNameEn } = storeToRefs(pageDataStore)
|
||||||
|
|
||||||
try {
|
if (!analytics || !pageName.value || !pageNameEn.value) {
|
||||||
const { gtag } = useGtag()
|
return {}
|
||||||
|
|
||||||
const eventName = analytics.event || ''
|
|
||||||
const eventLocale = analytics.eventLocale || ''
|
|
||||||
const eventCategory = `${analytics.eventCategory}`
|
|
||||||
|
|
||||||
// GA 클릭 이벤트 명 뒤에 언어 값 추가 노출되도록 개발. ex) GNB_자유게시판_KO
|
|
||||||
const eventLabel = `${eventCategory}_${eventLocale}`
|
|
||||||
|
|
||||||
gtag('set', 'cookie_domain', `${window?.location?.hostname || ''}`) // env 값으로 설정 시 쿠키 생성 안 돼서 window.location.hostname으로 설정
|
|
||||||
gtag('set', 'cookie_expires', '0') // 0으로 설정 시 쿠키가 Session 기반 쿠키로 전환
|
|
||||||
gtag('event', `${eventName}`, {
|
|
||||||
event_category: eventLabel,
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[Exception] useAnalytics.sendGA: ', e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const baseEvent = pageName.value
|
||||||
|
const baseViewArea = pageNameEn.value
|
||||||
|
let logData = {} as AnalyticsDetailType
|
||||||
|
|
||||||
|
// 문자열 'view'만 들어오는 경우 (페이지뷰)
|
||||||
|
if (analytics === 'view') {
|
||||||
|
logData = {
|
||||||
|
actionType: analytics,
|
||||||
|
event: baseEvent,
|
||||||
|
eventCategory: baseEvent,
|
||||||
|
viewArea: baseViewArea,
|
||||||
|
viewType: 'pageView',
|
||||||
|
} as AnalyticsDetailType
|
||||||
|
|
||||||
|
return logData
|
||||||
|
}
|
||||||
|
|
||||||
|
// 객체인 경우
|
||||||
|
if (typeof analytics === 'object') {
|
||||||
|
const { action_type, click_sarea, click_item } = analytics
|
||||||
|
|
||||||
|
logData = {
|
||||||
|
actionType: action_type,
|
||||||
|
event: baseEvent,
|
||||||
|
eventCategory: `${baseEvent}_${click_sarea}_${click_item}`,
|
||||||
|
} as AnalyticsDetailType
|
||||||
|
|
||||||
|
if (action_type === 'click') {
|
||||||
|
logData.clickArea = baseViewArea
|
||||||
|
logData.clickSarea = click_sarea
|
||||||
|
logData.clickItem = click_item
|
||||||
|
} else if (action_type === 'view') {
|
||||||
|
logData.viewArea = baseViewArea
|
||||||
|
logData.viewType = 'view_frame'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Stove Analytics(81 Plug) + Amplitude
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stove Analytics(81 Plug) 전송
|
* Stove Analytics(81 Plug) 전송
|
||||||
*
|
*
|
||||||
* @param {AnalyticsDetailType} analytics
|
* @param analytics Partial<AnalyticsDetailType>
|
||||||
* @param {string} mcode
|
* @param param1 mcode, options
|
||||||
* @param {object} options
|
|
||||||
*/
|
*/
|
||||||
const sendSA = (
|
const sendSA = (
|
||||||
analytics: AnalyticsDetailType,
|
analytics: Partial<AnalyticsDetailType>,
|
||||||
{ mcode = '', options = {} }: any
|
{ mcode = '', options = {} }: { mcode?: string; options?: any } = {}
|
||||||
) => {
|
) => {
|
||||||
if (!import.meta.client) return
|
if (!isClient()) return
|
||||||
|
if (isEmptyAnalytics(analytics)) return
|
||||||
|
|
||||||
const gameDataStore = useGameDataStore()
|
const gameDataStore = useGameDataStore()
|
||||||
const { gameData } = storeToRefs(gameDataStore)
|
const { gameData } = storeToRefs(gameDataStore)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const gameNo = gameData.value.game_code
|
|
||||||
|
|
||||||
const device = useDevice()
|
const device = useDevice()
|
||||||
const deviceType = device.isDesktop ? 'pcweb' : 'mobileweb'
|
|
||||||
|
|
||||||
|
const gameNo = gameData.value?.game_code
|
||||||
|
const deviceType = device.isDesktop ? 'pcweb' : 'mobileweb'
|
||||||
const country = `${csrGetCountry()}`
|
const country = `${csrGetCountry()}`
|
||||||
const memberNo = `${csrGetStoveMemberNo()}`
|
const memberNo = `${csrGetStoveMemberNo()}`
|
||||||
|
|
||||||
const actionType = analytics.actionType || ''
|
const {
|
||||||
const logSourceType = analytics.logSourceType || ''
|
actionType = '',
|
||||||
const viewArea = analytics.viewArea || ''
|
logSourceType = '',
|
||||||
const viewType = analytics.viewType || ''
|
viewArea = '',
|
||||||
const clickArea = analytics.clickArea || ''
|
viewType = '',
|
||||||
const clickSarea = findValueFromOption(analytics.clickSarea || '', {
|
clickArea = '',
|
||||||
options,
|
clickSarea = '',
|
||||||
})
|
eventLocale = '',
|
||||||
const eventLocale = analytics.eventLocale || ''
|
clickItem,
|
||||||
|
} = analytics
|
||||||
|
|
||||||
const identityInfo: IdentityInfo = {
|
const identityInfo: IdentityInfo = {
|
||||||
app_id: 'stove',
|
app_id: 'stove',
|
||||||
@@ -173,7 +162,7 @@ const sendSA = (
|
|||||||
media_page: '',
|
media_page: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
let actionParam = {}
|
let actionParam: ActionInfo['action_param'] = {}
|
||||||
|
|
||||||
if (actionType === 'view') {
|
if (actionType === 'view') {
|
||||||
actionParam = {
|
actionParam = {
|
||||||
@@ -190,7 +179,7 @@ const sendSA = (
|
|||||||
click_area: clickArea,
|
click_area: clickArea,
|
||||||
click_sarea: clickSarea,
|
click_sarea: clickSarea,
|
||||||
click_item: {
|
click_item: {
|
||||||
pwt_click_item: analytics.clickItem,
|
pwt_click_item: clickItem,
|
||||||
game_no: gameNo,
|
game_no: gameNo,
|
||||||
lang_cd: eventLocale,
|
lang_cd: eventLocale,
|
||||||
...options?.clickItem,
|
...options?.clickItem,
|
||||||
@@ -206,8 +195,8 @@ const sendSA = (
|
|||||||
|
|
||||||
const amplitudeActionInfo = {
|
const amplitudeActionInfo = {
|
||||||
...actionInfo,
|
...actionInfo,
|
||||||
url: import.meta.client ? `${location?.href || ''}` : '',
|
url: isClient() ? `${location?.href || ''}` : '',
|
||||||
agent: import.meta.client ? `${navigator?.userAgent || ''}` : '',
|
agent: isClient() ? `${navigator?.userAgent || ''}` : '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const amplitudeActionParams: {
|
const amplitudeActionParams: {
|
||||||
@@ -218,66 +207,107 @@ const sendSA = (
|
|||||||
event_properties: amplitudeActionInfo,
|
event_properties: amplitudeActionInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 81plug
|
||||||
svcLog.identity(identityInfo)
|
svcLog.identity(identityInfo)
|
||||||
svcLog.action(actionInfo, {}, {}) // 81plug warning log 제거를 위해 2번째 인자부터 빈 객체 세팅
|
// 81plug warning log 제거를 위해 2번째 인자부터 빈 객체 세팅
|
||||||
|
svcLog.action(actionInfo, {}, {})
|
||||||
|
|
||||||
|
// Amplitude
|
||||||
amplitude.track(amplitudeActionParams)
|
amplitude.track(amplitudeActionParams)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[Exception] useAnalytics.sendSA: ', e)
|
console.error('[Exception] useAnalytics.sendSA: ', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 기본 로그 일괄 전송
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 기본 로그 일괄 전송
|
* 기본 로그 일괄 전송
|
||||||
*
|
*
|
||||||
* @param {string} locale
|
* @param locale 언어 코드
|
||||||
* @param {AnalyticsDetailType} analytics
|
* @param analytics Partial<AnalyticsDetailType>
|
||||||
*/
|
*/
|
||||||
const sendLog = (locale: string, analytics: AnalyticsDetailType) => {
|
const sendLog = (locale: string, analytics: Partial<AnalyticsDetailType>) => {
|
||||||
|
if (isEmptyAnalytics(analytics)) return
|
||||||
|
|
||||||
// 언어 코드 대문자 변환
|
// 언어 코드 대문자 변환
|
||||||
analytics.eventLocale = locale.toUpperCase()
|
analytics.eventLocale = locale.toUpperCase()
|
||||||
|
|
||||||
if (analytics) {
|
// SA 전송 : actionType, logSourceType 유무로 판별 (logSourceType 체크는 주석 처리 되어있던 로직 유지)
|
||||||
// GA 전송 : eventCategory 유무로 판별
|
if (analytics.actionType && analytics.actionType !== '') {
|
||||||
sendGA(analytics, { options: analytics.options })
|
|
||||||
// SA 전송 : actionType, logSourceType 유무로 판별
|
|
||||||
if (
|
|
||||||
analytics.actionType &&
|
|
||||||
analytics.actionType !== ''
|
|
||||||
// analytics.logSourceType &&
|
|
||||||
// analytics.logSourceType !== ''
|
|
||||||
) {
|
|
||||||
sendSA(analytics, { mcode: analytics.mcode, options: analytics.options })
|
sendSA(analytics, { mcode: analytics.mcode, options: analytics.options })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Google Analytics
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/** GA 공통 설정 래퍼 */
|
||||||
|
const withGA = (callback: (gtag: any) => void) => {
|
||||||
|
if (!isClient()) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { gtag } = useGtag()
|
||||||
|
const hostname = window?.location?.hostname || ''
|
||||||
|
|
||||||
|
gtag('set', 'cookie_domain', hostname) // env 값으로 설정 시 쿠키 생성 안 돼서 window.location.hostname으로 설정
|
||||||
|
gtag('set', 'cookie_expires', '0') // 0으로 설정 시 쿠키가 Session 기반 쿠키로 전환
|
||||||
|
|
||||||
|
callback(gtag)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Exception] useAnalytics.withGA: ', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Google Analytics 전송 (기본 이벤트만 전송)
|
* Google Analytics 전송 (기본 이벤트만 전송)
|
||||||
*
|
*
|
||||||
* @param {string} gaEventName
|
* @param gaEventName 이벤트명
|
||||||
*/
|
*/
|
||||||
const sendGAEventOnly = (gaEventName: string) => {
|
const sendGAEventOnly = (gaEventName: string) => {
|
||||||
if (!import.meta.client) return
|
if (!gaEventName) return
|
||||||
|
|
||||||
try {
|
withGA(gtag => {
|
||||||
const { gtag } = useGtag()
|
|
||||||
|
|
||||||
gtag('set', 'cookie_domain', `${window?.location?.hostname || ''}`) // env 값으로 설정 시 쿠키 생성 안 돼서 window.location.hostname으로 설정
|
|
||||||
gtag('set', 'cookie_expires', '0') // 0으로 설정 시 쿠키가 Session 기반 쿠키로 전환
|
|
||||||
gtag('event', `${gaEventName}`)
|
gtag('event', `${gaEventName}`)
|
||||||
} catch (e) {
|
})
|
||||||
console.error('[Exception] useAnalytics.sendGAEventOnly: ', e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Analytics 전송
|
||||||
|
*
|
||||||
|
* @param analytics Partial<AnalyticsDetailType>
|
||||||
|
*/
|
||||||
|
const sendGA = (analytics: Partial<AnalyticsDetailType>) => {
|
||||||
|
if (isEmptyAnalytics(analytics)) return
|
||||||
|
|
||||||
|
withGA(gtag => {
|
||||||
|
const eventName = analytics.event || ''
|
||||||
|
const eventLocale = analytics.eventLocale || ''
|
||||||
|
const eventCategory = analytics.eventCategory || ''
|
||||||
|
// GA 클릭 이벤트 명 뒤에 언어 값 추가 노출되도록 개발. ex) GNB_자유게시판_KO
|
||||||
|
const eventLabel = `${eventCategory}_${eventLocale}`
|
||||||
|
|
||||||
|
gtag('event', `${eventName}`, {
|
||||||
|
event_category: eventLabel,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Meta / Twitter / TikTok Pixel
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메타 픽셀 전송
|
* 메타 픽셀 전송
|
||||||
*
|
*
|
||||||
* @param {string} fbEventName
|
* @param fbEventName 이벤트명
|
||||||
* @description 수집 대상 페이지에 useHead({ meta: [loadMetaPixelMeta()] }) 선언
|
* @description 수집 대상 페이지에 useHead({ meta: [loadMetaPixelMeta()] }) 선언
|
||||||
*/
|
*/
|
||||||
const sendMetaPixel = (fbEventName: string) => {
|
const sendMetaPixel = (fbEventName?: string) => {
|
||||||
if (!import.meta.client) return
|
if (!isClient() || !fbEventName) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { $fbq } = useNuxtApp()
|
const { $fbq } = useNuxtApp()
|
||||||
@@ -292,11 +322,11 @@ const sendMetaPixel = (fbEventName: string) => {
|
|||||||
/**
|
/**
|
||||||
* X(트위터) 픽셀 전송
|
* X(트위터) 픽셀 전송
|
||||||
*
|
*
|
||||||
* @param {string} twEventName
|
* @param twEventName 이벤트명
|
||||||
* @description 수집 대상 페이지에 useHead({ script: [loadTwitterPixelScript()] }) 선언
|
* @description 수집 대상 페이지에 useHead({ script: [loadTwitterPixelScript()] }) 선언
|
||||||
*/
|
*/
|
||||||
const sendTwitterPixel = (twEventName: string) => {
|
const sendTwitterPixel = (twEventName?: string) => {
|
||||||
if (!import.meta.client) return
|
if (!isClient() || !twEventName) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
twq('event', twEventName, {})
|
twq('event', twEventName, {})
|
||||||
@@ -308,11 +338,11 @@ const sendTwitterPixel = (twEventName: string) => {
|
|||||||
/**
|
/**
|
||||||
* 틱톡 픽셀 전송
|
* 틱톡 픽셀 전송
|
||||||
*
|
*
|
||||||
* @param {string} ttEventName
|
* @param ttEventName 이벤트명
|
||||||
* @description 수집 대상 페이지에 onMounted(() => { loadTikTokPixelScript() }) 선언
|
* @description 수집 대상 페이지에 onMounted(() => { loadTikTokPixelScript() }) 선언
|
||||||
*/
|
*/
|
||||||
const sendTiktokPixel = (ttEventName: string) => {
|
const sendTiktokPixel = (ttEventName?: string) => {
|
||||||
if (!import.meta.client) return
|
if (!isClient() || !ttEventName) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ttq.track(ttEventName)
|
ttq.track(ttEventName)
|
||||||
@@ -321,45 +351,61 @@ const sendTiktokPixel = (ttEventName: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 마케팅 스크립트 일괄 전송
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 마케팅 인텔리전스 팀 요청 마케팅 스크립트 일괄 전송
|
* 마케팅 인텔리전스 팀 요청 마케팅 스크립트 일괄 전송
|
||||||
*
|
*
|
||||||
* @param {string} gaEventName
|
* @param logName 'ga' | 'meta' | 'twitter' | 'tiktok'
|
||||||
* @param {string} fbEventName
|
* @param eventName 이벤트명 (ga: eventOnly, meta/twitter/tiktok: 필수)
|
||||||
* @param {string} twEventName
|
* @param analytics GA용 analytics 데이터 (eventName 없을 때만 사용)
|
||||||
* @param {string} ttEventName
|
|
||||||
*/
|
*/
|
||||||
const sendMarketingScript = ({
|
const sendMarketingLog = ({
|
||||||
gaEventName,
|
logName,
|
||||||
fbEventName,
|
eventName,
|
||||||
twEventName,
|
analytics,
|
||||||
ttEventName,
|
|
||||||
}: {
|
}: {
|
||||||
gaEventName?: string
|
logName: 'ga' | 'meta' | 'twitter' | 'tiktok'
|
||||||
fbEventName?: string
|
eventName?: string
|
||||||
twEventName?: string
|
analytics?: Partial<AnalyticsDetailType>
|
||||||
ttEventName?: string
|
|
||||||
}) => {
|
}) => {
|
||||||
if (gaEventName) {
|
switch (logName) {
|
||||||
sendGAEventOnly(gaEventName)
|
case 'ga': {
|
||||||
|
if (eventName) {
|
||||||
|
sendGAEventOnly(eventName)
|
||||||
|
} else if (analytics) {
|
||||||
|
sendGA(analytics)
|
||||||
}
|
}
|
||||||
if (fbEventName) {
|
break
|
||||||
sendMetaPixel(fbEventName)
|
|
||||||
}
|
}
|
||||||
if (twEventName) {
|
case 'meta': {
|
||||||
sendTwitterPixel(twEventName)
|
sendMetaPixel(eventName)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if (ttEventName) {
|
case 'twitter': {
|
||||||
sendTiktokPixel(ttEventName)
|
sendTwitterPixel(eventName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'tiktok': {
|
||||||
|
sendTiktokPixel(eventName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// export
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return {
|
return {
|
||||||
sendGA,
|
|
||||||
sendSA,
|
sendSA,
|
||||||
sendLog,
|
sendLog,
|
||||||
sendMarketingScript,
|
sendMarketingLog,
|
||||||
useAnalyticsLogDataDirect,
|
useAnalyticsData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import type { Splide as SplideType } from '@splidejs/splide'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Splide 슬라이더의 화살표 클릭 로직을 처리하는 composable
|
|
||||||
*/
|
|
||||||
export const useSplideArrow = () => {
|
|
||||||
/**
|
|
||||||
* 화살표 클릭 시 슬라이드 인덱스를 계산하는 함수
|
|
||||||
* @param direction - 이동 방향 ('prev' | 'next')
|
|
||||||
* @param splide - Splide 인스턴스
|
|
||||||
* @returns 다음 슬라이드 인덱스
|
|
||||||
*/
|
|
||||||
const calculateTargetIndex = (direction: 'prev' | 'next', splide: SplideType): number => {
|
|
||||||
const currentIndex = splide.index
|
|
||||||
const totalSlides = splide.length
|
|
||||||
|
|
||||||
if (direction === 'next') {
|
|
||||||
return currentIndex + 1 >= totalSlides ? 0 : currentIndex + 1
|
|
||||||
} else {
|
|
||||||
return currentIndex - 1 < 0 ? totalSlides - 1 : currentIndex - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 화살표 클릭 핸들러
|
|
||||||
* @param direction - 이동 방향
|
|
||||||
* @param splide - Splide 인스턴스
|
|
||||||
* @param onArrowClick - 화살표 클릭 시 실행될 콜백 함수
|
|
||||||
*/
|
|
||||||
const handleArrowClick = (
|
|
||||||
direction: 'prev' | 'next',
|
|
||||||
splide: SplideType,
|
|
||||||
onArrowClick?: (direction: 'prev' | 'next', targetIndex: number) => void
|
|
||||||
) => {
|
|
||||||
const targetIndex = calculateTargetIndex(direction, splide)
|
|
||||||
|
|
||||||
if (onArrowClick) {
|
|
||||||
onArrowClick(direction, targetIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 화살표 버튼에 클릭 이벤트 리스너를 추가하는 함수
|
|
||||||
* @param splide - Splide 인스턴스
|
|
||||||
* @param onArrowClick - 화살표 클릭 시 실행될 콜백 함수
|
|
||||||
* @returns 이벤트 리스너 제거 함수
|
|
||||||
*/
|
|
||||||
const addArrowClickListeners = (
|
|
||||||
splide: SplideType,
|
|
||||||
onArrowClick?: (direction: 'prev' | 'next', targetIndex: number) => void
|
|
||||||
) => {
|
|
||||||
const prevArrow = splide.root.querySelector('.arrow-prev')
|
|
||||||
const nextArrow = splide.root.querySelector('.arrow-next')
|
|
||||||
|
|
||||||
const prevHandler = () => handleArrowClick('prev', splide, onArrowClick)
|
|
||||||
const nextHandler = () => handleArrowClick('next', splide, onArrowClick)
|
|
||||||
|
|
||||||
if (prevArrow) {
|
|
||||||
prevArrow.addEventListener('click', prevHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextArrow) {
|
|
||||||
nextArrow.addEventListener('click', nextHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이벤트 리스너 제거 함수 반환
|
|
||||||
return () => {
|
|
||||||
if (prevArrow) {
|
|
||||||
prevArrow.removeEventListener('click', prevHandler)
|
|
||||||
}
|
|
||||||
if (nextArrow) {
|
|
||||||
nextArrow.removeEventListener('click', nextHandler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
calculateTargetIndex,
|
|
||||||
handleArrowClick,
|
|
||||||
addArrowClickListeners
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -14,16 +14,17 @@ import {
|
|||||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
||||||
import type { ReqCouponList } from '#layers/types/api/couponData'
|
import type { ReqCouponList } from '#layers/types/api/couponData'
|
||||||
|
|
||||||
// Props
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id?: string
|
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// Configuration
|
provide('pageVerTmplNameEn', props.pageVerTmplNameEn)
|
||||||
|
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
|
||||||
const stoveApiUrl = runtimeConfig.public.stoveApiUrl as string
|
const stoveApiUrl = runtimeConfig.public.stoveApiUrl as string
|
||||||
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
|
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
|
||||||
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_coupon.json'
|
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_coupon.json'
|
||||||
@@ -486,7 +487,6 @@ onMounted(async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<WidgetsFixMainTitle
|
<WidgetsFixMainTitle
|
||||||
:id="props.id"
|
|
||||||
:title="tm('Coupon_Page_Title')"
|
:title="tm('Coupon_Page_Title')"
|
||||||
:resources-data="backgroundData"
|
:resources-data="backgroundData"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import type { Platform } from '#layers/types/components/button'
|
|||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
id?: string
|
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
const runtimeConfig = useRuntimeConfig()
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
|
||||||
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
|
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
|
||||||
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_download.json'
|
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_download.json'
|
||||||
const stoveClientDownloadUrl = runtimeConfig.public
|
const stoveClientDownloadUrl = runtimeConfig.public
|
||||||
@@ -127,7 +128,6 @@ const handleMoveFocus = (target: 'pc' | 'mobile') => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<WidgetsFixMainTitle
|
<WidgetsFixMainTitle
|
||||||
:id="props.id"
|
|
||||||
:title="tm('Download_Page_Title')"
|
:title="tm('Download_Page_Title')"
|
||||||
:resources-data="backgroundData"
|
:resources-data="backgroundData"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type { Platform } from '#layers/types/components/button'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -362,7 +363,7 @@ const handlePreregistClick = () => {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<BlocksSlideDefault v-bind="splideOptions">
|
<WidgetsSlideDefault v-bind="splideOptions">
|
||||||
<SplideSlide
|
<SplideSlide
|
||||||
v-for="item in rewardImages"
|
v-for="item in rewardImages"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
@@ -380,7 +381,7 @@ const handlePreregistClick = () => {
|
|||||||
class="w-full h-full object-contain"
|
class="w-full h-full object-contain"
|
||||||
/>
|
/>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideDefault>
|
</WidgetsSlideDefault>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
<WidgetsDescription
|
<WidgetsDescription
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
id?: string
|
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
@@ -148,7 +148,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<WidgetsFixMainTitle
|
<WidgetsFixMainTitle
|
||||||
:id="props.id"
|
|
||||||
:title="tm('Secure_Page_Title') || '보안 강화 캠페인'"
|
:title="tm('Secure_Page_Title') || '보안 강화 캠페인'"
|
||||||
:resources-data="backgroundData"
|
:resources-data="backgroundData"
|
||||||
class="mx-auto"
|
class="mx-auto"
|
||||||
@@ -156,7 +155,9 @@ onMounted(() => {
|
|||||||
|
|
||||||
<div class="section-container static">
|
<div class="section-container static">
|
||||||
<section class="section-secure bg-[#F0F0F0] pb-50">
|
<section class="section-secure bg-[#F0F0F0] pb-50">
|
||||||
<div class="section-static content-standa flex-wrap md:max-w-[1300px] mx-auto">
|
<div
|
||||||
|
class="section-static content-standa flex-wrap md:max-w-[1300px] mx-auto"
|
||||||
|
>
|
||||||
<!-- Title Section (Non-Korean only) -->
|
<!-- Title Section (Non-Korean only) -->
|
||||||
<div
|
<div
|
||||||
v-if="locale !== 'ko'"
|
v-if="locale !== 'ko'"
|
||||||
@@ -170,7 +171,10 @@ onMounted(() => {
|
|||||||
<p
|
<p
|
||||||
class="text-[#666666] text-[13px] md:text-[14px] font-normal leading-[22px] md:leading-[24px] tracking-[-0.39px] md:tracking-[-0.42px]"
|
class="text-[#666666] text-[13px] md:text-[14px] font-normal leading-[22px] md:leading-[24px] tracking-[-0.39px] md:tracking-[-0.42px]"
|
||||||
>
|
>
|
||||||
*{{ tm('Secure_Global_Desc') || 'Set up the STOVE Authenticator to better protect your account.' }}
|
*{{
|
||||||
|
tm('Secure_Global_Desc') ||
|
||||||
|
'Set up the STOVE Authenticator to better protect your account.'
|
||||||
|
}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -282,7 +286,9 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="
|
:src="
|
||||||
formatPathHost(card.benefitIcon, { imageType: 'common' })
|
formatPathHost(card.benefitIcon, {
|
||||||
|
imageType: 'common',
|
||||||
|
})
|
||||||
"
|
"
|
||||||
:alt="card.benefitTitle"
|
:alt="card.benefitTitle"
|
||||||
:class="[
|
:class="[
|
||||||
|
|||||||
@@ -6,13 +6,17 @@ import {
|
|||||||
getComponentContainer,
|
getComponentContainer,
|
||||||
} from '#layers/utils/dataUtil'
|
} from '#layers/utils/dataUtil'
|
||||||
import { getYouTubeThumbnail } from '#layers/utils/youtubeUtil'
|
import { getYouTubeThumbnail } from '#layers/utils/youtubeUtil'
|
||||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
import type {
|
||||||
|
PageDataTemplateComponents,
|
||||||
|
PageDataResourceGroups,
|
||||||
|
} from '#layers/types/api/pageData'
|
||||||
import type { OperateGroupItem } from '#layers/types/api/operateResources'
|
import type { OperateGroupItem } from '#layers/types/api/operateResources'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id?: string
|
id?: string
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -49,6 +53,23 @@ const officialUrlData = computed(
|
|||||||
() => getComponentGroup(props.components, 'officialUrl')?.display?.text ?? ''
|
() => getComponentGroup(props.components, 'officialUrl')?.display?.text ?? ''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const arrowsData: PageDataResourceGroups = [
|
||||||
|
{
|
||||||
|
tracking: {
|
||||||
|
action_type: 'click',
|
||||||
|
click_item: '1. 컨텐츠 리스트 - 네비게이션(좌)',
|
||||||
|
click_sarea: 'Home_GameInfo__arrow',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tracking: {
|
||||||
|
action_type: 'click',
|
||||||
|
click_item: '1. 컨텐츠 리스트 - 네비게이션(우)',
|
||||||
|
click_sarea: 'Home_GameInfo__arrow',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const { data: slideData } = await useAsyncData(
|
const { data: slideData } = await useAsyncData(
|
||||||
`fx-video-01-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
|
`fx-video-01-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
|
||||||
async () => {
|
async () => {
|
||||||
@@ -137,11 +158,12 @@ const handleLoadMoreRecent = () => {
|
|||||||
<div
|
<div
|
||||||
class="relative content-static bg-[#fff] rounded-[12px] md:rounded-[16px]"
|
class="relative content-static bg-[#fff] rounded-[12px] md:rounded-[16px]"
|
||||||
>
|
>
|
||||||
<BlocksSlideFade
|
<WidgetsSlideFade
|
||||||
v-model:index="currentRecommendedIndex"
|
v-model:index="currentRecommendedIndex"
|
||||||
:autoplay="recommendedVideos.length > 1"
|
:autoplay="recommendedVideos.length > 1"
|
||||||
:interval="3000"
|
:interval="3000"
|
||||||
:arrows="recommendedVideos.length > 1"
|
:arrows="recommendedVideos.length > 1"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:drag="false"
|
:drag="false"
|
||||||
>
|
>
|
||||||
@@ -185,7 +207,7 @@ const handleLoadMoreRecent = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideFade>
|
</WidgetsSlideFade>
|
||||||
<div v-if="recommendedVideos.length > 1" class="splide-pagination">
|
<div v-if="recommendedVideos.length > 1" class="splide-pagination">
|
||||||
<span class="font-[700] text-[#1F1F1F]">
|
<span class="font-[700] text-[#1F1F1F]">
|
||||||
{{ currentRecommendedIndex + 1 }}
|
{{ currentRecommendedIndex + 1 }}
|
||||||
@@ -253,20 +275,20 @@ const handleLoadMoreRecent = () => {
|
|||||||
.splide {
|
.splide {
|
||||||
@apply pb-[68px] sm:pb-[0];
|
@apply pb-[68px] sm:pb-[0];
|
||||||
}
|
}
|
||||||
.splide:deep(.splide-arrows) {
|
.splide:deep(.splide__arrows) {
|
||||||
@apply block;
|
@apply block;
|
||||||
}
|
}
|
||||||
.splide:deep(.splide-arrows) .splide-arrow {
|
.splide:deep(.splide__arrows) .splide-arrow {
|
||||||
@apply block top-[unset] bottom-[20px] translate-y-0 bg-cover bg-center bg-no-repeat
|
@apply block top-[unset] bottom-[20px] translate-y-0 bg-cover bg-center bg-no-repeat
|
||||||
sm:bottom-[24px] md:bottom-[36px] lg:bottom-[60px];
|
sm:bottom-[24px] md:bottom-[36px] lg:bottom-[60px];
|
||||||
}
|
}
|
||||||
.splide:deep(.splide-arrows) .arrow-prev {
|
.splide:deep(.splide__arrows) .splide__arrow--prev {
|
||||||
@apply left-[20px] bg-[image:url('/images/common/btn_system_arrow_prev.png')]
|
@apply left-[20px] bg-[image:url('/images/common/btn_system_arrow_prev.png')]
|
||||||
sm:left-[calc(60.3%+21px)]
|
sm:left-[calc(60.3%+21px)]
|
||||||
md:left-[calc(56%+39px)]
|
md:left-[calc(56%+39px)]
|
||||||
lg:left-[790px];
|
lg:left-[790px];
|
||||||
}
|
}
|
||||||
.splide:deep(.splide-arrows) .arrow-next {
|
.splide:deep(.splide__arrows) .splide__arrow--next {
|
||||||
@apply right-[20px] bg-[image:url('/images/common/btn_system_arrow_next.png')]
|
@apply right-[20px] bg-[image:url('/images/common/btn_system_arrow_next.png')]
|
||||||
sm:right-[28px]
|
sm:right-[28px]
|
||||||
md:right-[unset] md:left-[calc(56%+99px)]
|
md:right-[unset] md:left-[calc(56%+99px)]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type { CwmsArticleItem } from '#layers/types/api/cwmsArticle'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -15,7 +16,6 @@ const props = defineProps<Props>()
|
|||||||
const gameDataStore = useGameDataStore()
|
const gameDataStore = useGameDataStore()
|
||||||
const pageDataStore = usePageDataStore()
|
const pageDataStore = usePageDataStore()
|
||||||
const { getCwmsArticle } = useCwmsArticle()
|
const { getCwmsArticle } = useCwmsArticle()
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
|
|
||||||
const { gameData } = storeToRefs(gameDataStore)
|
const { gameData } = storeToRefs(gameDataStore)
|
||||||
@@ -24,19 +24,23 @@ const { pageData } = storeToRefs(pageDataStore)
|
|||||||
const boardId = computed(
|
const boardId = computed(
|
||||||
() => getComponentGroup(props.components, 'boardId')?.display?.text
|
() => getComponentGroup(props.components, 'boardId')?.display?.text
|
||||||
)
|
)
|
||||||
const paginationData = computed(() => {
|
|
||||||
return getComponentGroupAry(props.components, 'pagination')
|
|
||||||
})
|
|
||||||
const backgroundData = computed(() =>
|
const backgroundData = computed(() =>
|
||||||
getComponentGroup(props.components, 'background')
|
getComponentGroup(props.components, 'background')
|
||||||
)
|
)
|
||||||
const mainTitleData = computed(() =>
|
const mainTitleData = computed(() =>
|
||||||
getComponentGroup(props.components, 'mainTitle')
|
getComponentGroup(props.components, 'mainTitle')
|
||||||
)
|
)
|
||||||
const buttonListData = ref(getComponentGroupAry(props.components, 'buttonList'))
|
|
||||||
const descriptionData = computed(() =>
|
const descriptionData = computed(() =>
|
||||||
getComponentGroup(props.components, 'description')
|
getComponentGroup(props.components, 'description')
|
||||||
)
|
)
|
||||||
|
const arrowsData = computed(() =>
|
||||||
|
getComponentGroupAry(props.components, 'arrow')
|
||||||
|
)
|
||||||
|
const paginationData = computed(() => {
|
||||||
|
return getComponentGroupAry(props.components, 'pagination')
|
||||||
|
})
|
||||||
|
|
||||||
|
const buttonListData = ref(getComponentGroupAry(props.components, 'buttonList'))
|
||||||
|
|
||||||
const { data: slideData } = await useAsyncData(
|
const { data: slideData } = await useAsyncData(
|
||||||
`gr-board-01-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
|
`gr-board-01-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
|
||||||
@@ -103,12 +107,6 @@ const getArticleUrl = (articleId: string) => {
|
|||||||
}
|
}
|
||||||
return `${communityUrl}/view/${articleId}`
|
return `${communityUrl}/view/${articleId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const onArrowClick = (direction, targetIndex) => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -124,12 +122,12 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
class="title-md max-w-[944px] mx-[20px] sm:mx-[40px]"
|
class="title-md max-w-[944px] mx-[20px] sm:mx-[40px]"
|
||||||
/>
|
/>
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<BlocksSlideDefault
|
<WidgetsSlideDefault
|
||||||
v-if="slideLength > 0"
|
v-if="slideLength > 0"
|
||||||
:slide-item-length="slideLength"
|
|
||||||
v-bind="splideOptions"
|
v-bind="splideOptions"
|
||||||
|
:slide-item-length="slideLength"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@arrow-click="onArrowClick"
|
|
||||||
>
|
>
|
||||||
<SplideSlide
|
<SplideSlide
|
||||||
v-for="(item, index) in slideData"
|
v-for="(item, index) in slideData"
|
||||||
@@ -141,16 +139,16 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
:description="globalDateFormat(item.create_datetime, locale)"
|
:description="globalDateFormat(item.create_datetime, locale)"
|
||||||
:img-path="item.media_thumbnail_url"
|
:img-path="item.media_thumbnail_url"
|
||||||
:url="getArticleUrl(item.article_id)"
|
:url="getArticleUrl(item.article_id)"
|
||||||
|
:analytics-sarea="pageVerTmplNameEn"
|
||||||
link-target="_blank"
|
link-target="_blank"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideDefault>
|
</WidgetsSlideDefault>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="buttonListData"
|
v-if="buttonListData"
|
||||||
:resources-data="buttonListData"
|
:resources-data="buttonListData"
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
class="mt-[48px] md:mt-[64px]"
|
class="mt-[48px] md:mt-[64px]"
|
||||||
/>
|
/>
|
||||||
<WidgetsDescription
|
<WidgetsDescription
|
||||||
@@ -166,10 +164,10 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
.splide {
|
.splide {
|
||||||
@apply mt-[24px] md:max-w-[776px] md:mt-[48px] md:mx-auto md:px-[72px] lg:max-w-[1428px];
|
@apply mt-[24px] md:max-w-[776px] md:mt-[48px] md:mx-auto md:px-[72px] lg:max-w-[1428px];
|
||||||
}
|
}
|
||||||
.splide:deep(.arrow-prev) {
|
.splide:deep(.arsplide__arrow--prev) {
|
||||||
@apply top-[calc(50%-28px)] left-[0];
|
@apply top-[calc(50%-28px)] left-[0];
|
||||||
}
|
}
|
||||||
.splide:deep(.arrow-next) {
|
.splide:deep(.splide__arrow--next) {
|
||||||
@apply top-[calc(50%-28px)] right-[0];
|
@apply top-[calc(50%-28px)] right-[0];
|
||||||
}
|
}
|
||||||
.slide-inner {
|
.slide-inner {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -56,7 +57,6 @@ const buttonListData = computed(() => {
|
|||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="buttonListData"
|
v-if="buttonListData"
|
||||||
:resources-data="buttonListData"
|
:resources-data="buttonListData"
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
class="mt-[56px]"
|
class="mt-[56px]"
|
||||||
/>
|
/>
|
||||||
<WidgetsDescription
|
<WidgetsDescription
|
||||||
|
|||||||
@@ -9,10 +9,14 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const { locale } = useI18n()
|
||||||
|
const { sendLog, useAnalyticsData } = useAnalytics()
|
||||||
|
|
||||||
const splideRef = ref<SplideSlide | null>(null)
|
const splideRef = ref<SplideSlide | null>(null)
|
||||||
const currentSlideIndex = ref<number | null>(null)
|
const currentSlideIndex = ref<number | null>(null)
|
||||||
|
|
||||||
@@ -23,17 +27,24 @@ const paginationData = computed(() => {
|
|||||||
return getComponentGroupAry(props.components, 'pagination')
|
return getComponentGroupAry(props.components, 'pagination')
|
||||||
})
|
})
|
||||||
|
|
||||||
const goToSlide = (index: number) => {
|
const goToSlide = (index: number, title: string) => {
|
||||||
const splide = splideRef.value?.splide
|
const splide = splideRef.value?.splide
|
||||||
|
const paginationAnalytics = {
|
||||||
|
action_type: 'click',
|
||||||
|
click_item: title,
|
||||||
|
click_sarea: props.pageVerTmplNameEn,
|
||||||
|
}
|
||||||
|
|
||||||
if (splide) {
|
if (splide) {
|
||||||
splide.go(index)
|
splide.go(index)
|
||||||
|
sendLog(locale.value, useAnalyticsData(paginationAnalytics))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="section-standard">
|
<section class="section-standard">
|
||||||
<BlocksSlideFade
|
<WidgetsSlideFade
|
||||||
v-if="slideData"
|
v-if="slideData"
|
||||||
ref="splideRef"
|
ref="splideRef"
|
||||||
v-model:index="currentSlideIndex"
|
v-model:index="currentSlideIndex"
|
||||||
@@ -66,7 +77,7 @@ const goToSlide = (index: number) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideFade>
|
</WidgetsSlideFade>
|
||||||
<div
|
<div
|
||||||
v-if="slideData && slideData.length > 1"
|
v-if="slideData && slideData.length > 1"
|
||||||
class="splide-pagination"
|
class="splide-pagination"
|
||||||
@@ -88,7 +99,12 @@ const goToSlide = (index: number) => {
|
|||||||
'btn-pagination',
|
'btn-pagination',
|
||||||
{ 'is-active': currentSlideIndex === index },
|
{ 'is-active': currentSlideIndex === index },
|
||||||
]"
|
]"
|
||||||
@click="goToSlide(index)"
|
@click="
|
||||||
|
goToSlide(
|
||||||
|
index,
|
||||||
|
getComponentGroup(item, 'pagenaviTitle')?.display?.text || ''
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<span class="item-bullet"></span>
|
<span class="item-bullet"></span>
|
||||||
<span class="item-title">
|
<span class="item-title">
|
||||||
@@ -105,7 +121,7 @@ const goToSlide = (index: number) => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.splide-pagination {
|
.splide-pagination {
|
||||||
@apply overflow-hidden flex items-center justify-center absolute bottom-10 left-1/2 max-w-full -translate-x-1/2 z-[5] md:bottom-[96px];
|
@apply flex items-center justify-center absolute bottom-10 left-1/2 max-w-full -translate-x-1/2 z-[5] md:bottom-[96px];
|
||||||
}
|
}
|
||||||
.btn-pagination {
|
.btn-pagination {
|
||||||
@apply relative;
|
@apply relative;
|
||||||
@@ -122,7 +138,7 @@ const goToSlide = (index: number) => {
|
|||||||
color: var(--pagination-disabled);
|
color: var(--pagination-disabled);
|
||||||
}
|
}
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
@apply relative w-[68px] h-[1px] overflow-hidden md:w-[184px];
|
@apply relative w-[62px] h-[1px] overflow-hidden xs:w-[68px] md:w-[184px];
|
||||||
background-color: var(--pagination-disabled);
|
background-color: var(--pagination-disabled);
|
||||||
}
|
}
|
||||||
.progress-fill {
|
.progress-fill {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -23,6 +24,9 @@ const thumbnailData = computed(() => {
|
|||||||
.map(item => item?.pagenaviThumbnail?.groups?.[0])
|
.map(item => item?.pagenaviThumbnail?.groups?.[0])
|
||||||
.filter((group): group is NonNullable<typeof group> => group != null)
|
.filter((group): group is NonNullable<typeof group> => group != null)
|
||||||
})
|
})
|
||||||
|
const arrowsData = computed(() => {
|
||||||
|
return getComponentGroupAry(props.components, 'arrow')
|
||||||
|
})
|
||||||
const paginationData = computed(() => {
|
const paginationData = computed(() => {
|
||||||
return getComponentGroupAry(props.components, 'pagination')
|
return getComponentGroupAry(props.components, 'pagination')
|
||||||
})
|
})
|
||||||
@@ -30,10 +34,12 @@ const paginationData = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="section-standard">
|
<section class="section-standard">
|
||||||
<BlocksSlideThumbnail
|
<WidgetsSlideThumbnail
|
||||||
v-model:index="currentSlideIndex"
|
v-model:index="currentSlideIndex"
|
||||||
:thumbnail-data="thumbnailData"
|
:thumbnail-data="thumbnailData"
|
||||||
:pagination-data="paginationData"
|
:pagination-data="paginationData"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
|
:analytics-sarea="props.pageVerTmplNameEn"
|
||||||
>
|
>
|
||||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||||
<WidgetsBackground
|
<WidgetsBackground
|
||||||
@@ -59,6 +65,6 @@ const paginationData = computed(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideThumbnail>
|
</WidgetsSlideThumbnail>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import type {
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -32,6 +33,9 @@ const thumbnailData = computed(() => {
|
|||||||
.map(item => item?.pagenaviThumbnail?.groups?.[0])
|
.map(item => item?.pagenaviThumbnail?.groups?.[0])
|
||||||
.filter((group): group is NonNullable<typeof group> => group != null)
|
.filter((group): group is NonNullable<typeof group> => group != null)
|
||||||
})
|
})
|
||||||
|
const arrowsData = computed(() => {
|
||||||
|
return getComponentGroupAry(props.components, 'arrow')
|
||||||
|
})
|
||||||
const paginationData = computed(() => {
|
const paginationData = computed(() => {
|
||||||
return getComponentGroupAry(props.components, 'pagination')
|
return getComponentGroupAry(props.components, 'pagination')
|
||||||
})
|
})
|
||||||
@@ -44,10 +48,12 @@ const getVideoSrc = (item: PageDataTemplateComponent) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="section-standard">
|
<section class="section-standard">
|
||||||
<BlocksSlideThumbnail
|
<WidgetsSlideThumbnail
|
||||||
v-model:index="currentSlideIndex"
|
v-model:index="currentSlideIndex"
|
||||||
:thumbnail-data="thumbnailData"
|
:thumbnail-data="thumbnailData"
|
||||||
:pagination-data="paginationData"
|
:pagination-data="paginationData"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
|
:analytics-sarea="props.pageVerTmplNameEn"
|
||||||
>
|
>
|
||||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||||
<WidgetsBackground
|
<WidgetsBackground
|
||||||
@@ -91,6 +97,6 @@ const getVideoSrc = (item: PageDataTemplateComponent) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideThumbnail>
|
</WidgetsSlideThumbnail>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -12,13 +12,11 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const { locale } = useI18n()
|
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
|
|
||||||
const playingSlideIndex = ref<number | null>(null)
|
const playingSlideIndex = ref<number | null>(null)
|
||||||
let stopVideoTimeoutId: ReturnType<typeof setTimeout> | null = null
|
let stopVideoTimeoutId: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
@@ -36,32 +34,26 @@ const slideData = computed(() => {
|
|||||||
.map(item => item.media?.groups?.[0])
|
.map(item => item.media?.groups?.[0])
|
||||||
.filter((group): group is NonNullable<typeof group> => group != null)
|
.filter((group): group is NonNullable<typeof group> => group != null)
|
||||||
})
|
})
|
||||||
|
const arrowsData = computed(() => {
|
||||||
|
return getComponentGroupAry(props.components, 'arrow')
|
||||||
|
})
|
||||||
const paginationData = computed(() => {
|
const paginationData = computed(() => {
|
||||||
return getComponentGroupAry(props.components, 'pagination')
|
return getComponentGroupAry(props.components, 'pagination')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getVideoPlayTracking = (item: string) => {
|
||||||
|
return {
|
||||||
|
action_type: 'click',
|
||||||
|
click_item: `${item}_영상재생`,
|
||||||
|
click_sarea: props.pageVerTmplNameEn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleVideoClick = (index: number) => {
|
const handleVideoClick = (index: number) => {
|
||||||
playingSlideIndex.value = index
|
playingSlideIndex.value = index
|
||||||
|
|
||||||
const group = getComponentGroup(props.components, 'videoPlay')
|
const group = getComponentGroup(props.components, 'videoPlay')
|
||||||
if (!group || !group.tracking) return
|
if (!group || !group.tracking) return
|
||||||
|
|
||||||
const base = group.tracking.click_item || ''
|
|
||||||
const next = base
|
|
||||||
? base.replace(/(^.*_)(\d+)$/, `$1${index}`) === base
|
|
||||||
? `${base}_${index}`
|
|
||||||
: base.replace(/(^.*_)(\d+)$/, `$1${index}`)
|
|
||||||
: `${index}`
|
|
||||||
|
|
||||||
const sendingGroup = {
|
|
||||||
...group,
|
|
||||||
tracking: { ...group.tracking, click_item: next },
|
|
||||||
}
|
|
||||||
|
|
||||||
sendLog(
|
|
||||||
locale.value,
|
|
||||||
useAnalyticsLogDataDirect(sendingGroup, props.pageVerTmplSeq)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSplideMove = () => {
|
const handleSplideMove = () => {
|
||||||
@@ -78,12 +70,6 @@ const handleSplideMove = () => {
|
|||||||
}, 600)
|
}, 600)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onArrowClick = (direction, _targetIndex) => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
// 타이머 정리
|
// 타이머 정리
|
||||||
if (stopVideoTimeoutId) {
|
if (stopVideoTimeoutId) {
|
||||||
@@ -102,14 +88,15 @@ onBeforeUnmount(() => {
|
|||||||
:resources-data="mainTitleData"
|
:resources-data="mainTitleData"
|
||||||
class="title-md max-w-[944px]"
|
class="title-md max-w-[944px]"
|
||||||
/>
|
/>
|
||||||
<BlocksSlideThumbnail
|
<WidgetsSlideThumbnail
|
||||||
:drag="false"
|
|
||||||
:thumbnail-data="slideData"
|
:thumbnail-data="slideData"
|
||||||
|
:pagination-data="paginationData"
|
||||||
|
:drag="false"
|
||||||
variant="media"
|
variant="media"
|
||||||
class="mt-[24px] md:mt-[32px]"
|
class="mt-[24px] md:mt-[32px]"
|
||||||
:pagination-data="paginationData"
|
|
||||||
:arrows="slideData.length > 5"
|
:arrows="slideData.length > 5"
|
||||||
@arrow-click="onArrowClick"
|
:arrows-data="arrowsData"
|
||||||
|
:analytics-sarea="props.pageVerTmplNameEn"
|
||||||
@move="handleSplideMove"
|
@move="handleSplideMove"
|
||||||
>
|
>
|
||||||
<SplideSlide
|
<SplideSlide
|
||||||
@@ -128,6 +115,7 @@ onBeforeUnmount(() => {
|
|||||||
<AtomsButtonPlay
|
<AtomsButtonPlay
|
||||||
v-if="playingSlideIndex !== index"
|
v-if="playingSlideIndex !== index"
|
||||||
class="btn-play"
|
class="btn-play"
|
||||||
|
:tracking="getVideoPlayTracking(item?.group_label)"
|
||||||
@click="handleVideoClick(index)"
|
@click="handleVideoClick(index)"
|
||||||
/>
|
/>
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
@@ -142,7 +130,7 @@ onBeforeUnmount(() => {
|
|||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideThumbnail>
|
</WidgetsSlideThumbnail>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -23,6 +24,9 @@ const mainTitleData = computed(() =>
|
|||||||
const slideData = computed(() => {
|
const slideData = computed(() => {
|
||||||
return getComponentContainer(props.components, 'group_sets', { minLength: 4 })
|
return getComponentContainer(props.components, 'group_sets', { minLength: 4 })
|
||||||
})
|
})
|
||||||
|
const arrowsData = computed(() =>
|
||||||
|
getComponentGroupAry(props.components, 'arrow')
|
||||||
|
)
|
||||||
|
|
||||||
const buttonListData = ref(
|
const buttonListData = ref(
|
||||||
getComponentGroupAry(slideData?.value[0], 'buttonList')
|
getComponentGroupAry(slideData?.value[0], 'buttonList')
|
||||||
@@ -49,13 +53,6 @@ const handleSplideMove = (_splide: SplideType, newIndex: number) => {
|
|||||||
'buttonList'
|
'buttonList'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const { locale } = useI18n()
|
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
const onArrowClick = (direction, targetIndex) => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -71,14 +68,14 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
:resources-data="mainTitleData"
|
:resources-data="mainTitleData"
|
||||||
class="title-md max-w-[944px] mx-[20px] sm:mx-[40px]"
|
class="title-md max-w-[944px] mx-[20px] sm:mx-[40px]"
|
||||||
/>
|
/>
|
||||||
<BlocksSlideCenterFocus
|
<WidgetsSlideCenterFocus
|
||||||
v-if="slideData"
|
v-if="slideData"
|
||||||
:slide-item-size="slideItemSize"
|
:slide-item-size="slideItemSize"
|
||||||
:slide-item-length="slideData?.length"
|
:slide-item-length="slideData?.length"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
class="mt-[24px] md:mt-[48px]"
|
class="mt-[24px] md:mt-[48px]"
|
||||||
@move="handleSplideMove"
|
@move="handleSplideMove"
|
||||||
@arrow-click="onArrowClick"
|
|
||||||
>
|
>
|
||||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||||
<div class="slide-inner border-line mt-auto">
|
<div class="slide-inner border-line mt-auto">
|
||||||
@@ -89,11 +86,10 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideCenterFocus>
|
</WidgetsSlideCenterFocus>
|
||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="buttonListData"
|
v-if="buttonListData"
|
||||||
:resources-data="buttonListData"
|
:resources-data="buttonListData"
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
class="mt-[40px] md:mt-[56px]"
|
class="mt-[40px] md:mt-[56px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -23,6 +24,10 @@ const mainTitleData = computed(() =>
|
|||||||
const slideData = computed(() =>
|
const slideData = computed(() =>
|
||||||
getComponentContainer(props.components, 'group_sets', { minLength: 4 })
|
getComponentContainer(props.components, 'group_sets', { minLength: 4 })
|
||||||
)
|
)
|
||||||
|
const arrowsData = computed(() =>
|
||||||
|
getComponentGroupAry(props.components, 'arrow')
|
||||||
|
)
|
||||||
|
|
||||||
const imgTitleData = ref(getComponentGroup(slideData?.value[0], 'imgTitle'))
|
const imgTitleData = ref(getComponentGroup(slideData?.value[0], 'imgTitle'))
|
||||||
const descriptionData = ref(
|
const descriptionData = ref(
|
||||||
getComponentGroup(slideData?.value[0], 'description')
|
getComponentGroup(slideData?.value[0], 'description')
|
||||||
@@ -60,13 +65,6 @@ const handleSplideMove = (
|
|||||||
'buttonList'
|
'buttonList'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const { locale } = useI18n()
|
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
const onArrowClick = (direction, targetIndex) => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -80,14 +78,14 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
:resources-data="mainTitleData"
|
:resources-data="mainTitleData"
|
||||||
class="title-md max-w-[944px] mx-[20px] sm:mx-[40px]"
|
class="title-md max-w-[944px] mx-[20px] sm:mx-[40px]"
|
||||||
/>
|
/>
|
||||||
<BlocksSlideCenterHighlight
|
<WidgetsSlideCenterHighlight
|
||||||
v-if="slideData"
|
v-if="slideData"
|
||||||
:slide-item-size="slideItemSize"
|
:slide-item-size="slideItemSize"
|
||||||
:slide-item-length="slideData?.length"
|
:slide-item-length="slideData?.length"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
class="mt-[24px] md:mt-[48px]"
|
class="mt-[24px] md:mt-[48px]"
|
||||||
@move="handleSplideMove"
|
@move="handleSplideMove"
|
||||||
@arrow-click="onArrowClick"
|
|
||||||
>
|
>
|
||||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||||
<div class="slide-inner border-line">
|
<div class="slide-inner border-line">
|
||||||
@@ -98,7 +96,7 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideCenterHighlight>
|
</WidgetsSlideCenterHighlight>
|
||||||
<WidgetsSubTitle
|
<WidgetsSubTitle
|
||||||
v-if="imgTitleData"
|
v-if="imgTitleData"
|
||||||
:resources-data="imgTitleData"
|
:resources-data="imgTitleData"
|
||||||
@@ -112,7 +110,6 @@ const onArrowClick = (direction, targetIndex) => {
|
|||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="buttonListData"
|
v-if="buttonListData"
|
||||||
:resources-data="buttonListData"
|
:resources-data="buttonListData"
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
class="mt-[32px] mx-[20px] sm:mx-[40px]"
|
class="mt-[32px] mx-[20px] sm:mx-[40px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -44,15 +45,10 @@ const buttonListData = computed(() =>
|
|||||||
:resources-data="descriptionData"
|
:resources-data="descriptionData"
|
||||||
class="w-full max-w-[355px] md:max-w-[944px]"
|
class="w-full max-w-[355px] md:max-w-[944px]"
|
||||||
/>
|
/>
|
||||||
<WidgetsVideoPlay
|
<WidgetsVideoPlay v-if="videoPlayData" :resources-data="videoPlayData" />
|
||||||
v-if="videoPlayData"
|
|
||||||
:resources-data="videoPlayData"
|
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
/>
|
|
||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="buttonListData"
|
v-if="buttonListData"
|
||||||
:resources-data="buttonListData"
|
:resources-data="buttonListData"
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
class="mt-[22px] md:mt-[52px]"
|
class="mt-[22px] md:mt-[52px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { OperateGroupItem } from '#layers/types/api/operateResources'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
@@ -19,7 +20,6 @@ const props = defineProps<Props>()
|
|||||||
const pageDataStore = usePageDataStore()
|
const pageDataStore = usePageDataStore()
|
||||||
const { getOperateResources } = useOperateResources()
|
const { getOperateResources } = useOperateResources()
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
|
|
||||||
const { pageData } = storeToRefs(pageDataStore)
|
const { pageData } = storeToRefs(pageDataStore)
|
||||||
|
|
||||||
@@ -35,6 +35,9 @@ const descriptionData = computed(() =>
|
|||||||
const videoPlayData = computed(() =>
|
const videoPlayData = computed(() =>
|
||||||
getComponentGroup(props.components, 'videoPlay')
|
getComponentGroup(props.components, 'videoPlay')
|
||||||
)
|
)
|
||||||
|
const arrowsData = computed(() =>
|
||||||
|
getComponentGroupAry(props.components, 'arrow')
|
||||||
|
)
|
||||||
|
|
||||||
const { data: slideData } = await useAsyncData(
|
const { data: slideData } = await useAsyncData(
|
||||||
`gr-visual-02-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
|
`gr-visual-02-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
|
||||||
@@ -75,12 +78,6 @@ const slideItemSize = {
|
|||||||
gap: 32,
|
gap: 32,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const onArrowClick = direction => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -101,18 +98,14 @@ const onArrowClick = direction => {
|
|||||||
:resources-data="descriptionData"
|
:resources-data="descriptionData"
|
||||||
class="w-full max-w-[355px] mx-[20px] sm:mx-[40px] md:max-w-[944px]"
|
class="w-full max-w-[355px] mx-[20px] sm:mx-[40px] md:max-w-[944px]"
|
||||||
/>
|
/>
|
||||||
<WidgetsVideoPlay
|
<WidgetsVideoPlay v-if="videoPlayData" :resources-data="videoPlayData" />
|
||||||
v-if="videoPlayData"
|
<WidgetsSlideCenterHighlight
|
||||||
:resources-data="videoPlayData"
|
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
/>
|
|
||||||
<BlocksSlideCenterHighlight
|
|
||||||
v-if="slideData && slideData.length > 0"
|
v-if="slideData && slideData.length > 0"
|
||||||
:slide-item-size="slideItemSize"
|
:slide-item-size="slideItemSize"
|
||||||
:slide-item-length="slideData.length"
|
:slide-item-length="slideData.length"
|
||||||
|
:arrows-data="arrowsData"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
class="mt-[36px] md:mt-[60px]"
|
class="mt-[36px] md:mt-[60px]"
|
||||||
@arrow-click="onArrowClick"
|
|
||||||
>
|
>
|
||||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||||
<BlocksCardNews
|
<BlocksCardNews
|
||||||
@@ -121,10 +114,11 @@ const onArrowClick = direction => {
|
|||||||
:img-path="formatPathHost(item.img_path)"
|
:img-path="formatPathHost(item.img_path)"
|
||||||
:url="item.url"
|
:url="item.url"
|
||||||
:link-target="item.link_target"
|
:link-target="item.link_target"
|
||||||
|
:analytics-sarea="`${pageVerTmplNameEn}_bannerList`"
|
||||||
class="slide-inner"
|
class="slide-inner"
|
||||||
/>
|
/>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideCenterHighlight>
|
</WidgetsSlideCenterHighlight>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,39 +11,34 @@ import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
|||||||
interface Props {
|
interface Props {
|
||||||
components: PageDataTemplateComponents
|
components: PageDataTemplateComponents
|
||||||
pageVerTmplSeq: number
|
pageVerTmplSeq: number
|
||||||
|
pageVerTmplNameEn: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const { locale } = useI18n()
|
|
||||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
|
||||||
|
|
||||||
const currentSlideIndex = ref<number | null>(null)
|
const currentSlideIndex = ref<number | null>(null)
|
||||||
|
|
||||||
const slideData = computed(() => {
|
const slideData = computed(() => {
|
||||||
return getComponentContainer(props.components, 'group_sets')
|
return getComponentContainer(props.components, 'group_sets')
|
||||||
})
|
})
|
||||||
|
const arrowsData = computed(() => {
|
||||||
|
return getComponentGroupAry(props.components, 'arrow')
|
||||||
|
})
|
||||||
const paginationData = computed(() => {
|
const paginationData = computed(() => {
|
||||||
return getComponentGroupAry(props.components, 'pagination')
|
return getComponentGroupAry(props.components, 'pagination')
|
||||||
})
|
})
|
||||||
|
|
||||||
const onArrowClick = direction => {
|
|
||||||
const arrowGroupAry = getComponentGroupAry(props.components, 'arrow')
|
|
||||||
const logTracking = arrowGroupAry?.[direction === 'prev' ? 0 : 1]
|
|
||||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTracking, 1))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="section-standard">
|
<section class="section-standard">
|
||||||
<BlocksSlideFade
|
<WidgetsSlideFade
|
||||||
v-if="slideData"
|
v-if="slideData"
|
||||||
v-model:index="currentSlideIndex"
|
v-model:index="currentSlideIndex"
|
||||||
:arrows="slideData.length > 1"
|
:arrows="slideData.length > 1"
|
||||||
:pagination="slideData.length > 1"
|
:pagination="slideData.length > 1"
|
||||||
class="h-full"
|
|
||||||
:pagination-data="paginationData"
|
:pagination-data="paginationData"
|
||||||
@arrow-click="onArrowClick"
|
:arrows-data="arrowsData"
|
||||||
|
class="h-full"
|
||||||
>
|
>
|
||||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||||
<WidgetsBackground
|
<WidgetsBackground
|
||||||
@@ -70,11 +65,10 @@ const onArrowClick = direction => {
|
|||||||
<WidgetsButtonList
|
<WidgetsButtonList
|
||||||
v-if="hasComponentGroup(item, 'buttonList')"
|
v-if="hasComponentGroup(item, 'buttonList')"
|
||||||
:resources-data="getComponentGroupAry(item, 'buttonList')"
|
:resources-data="getComponentGroupAry(item, 'buttonList')"
|
||||||
:page-ver-tmpl-seq="props.pageVerTmplSeq"
|
|
||||||
class="mt-[28px] md:mt-[52px]"
|
class="mt-[28px] md:mt-[52px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SplideSlide>
|
</SplideSlide>
|
||||||
</BlocksSlideFade>
|
</WidgetsSlideFade>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface AnalyticsMetaType {
|
|||||||
|
|
||||||
interface AnalyticsType {
|
interface AnalyticsType {
|
||||||
analyticsNo: number
|
analyticsNo: number
|
||||||
gameId: string
|
gameId?: string
|
||||||
analyticsType?: AnalyticsMetaType
|
analyticsType?: AnalyticsMetaType
|
||||||
analyticsVersion?: AnalyticsMetaType
|
analyticsVersion?: AnalyticsMetaType
|
||||||
analyticsCode?: string
|
analyticsCode?: string
|
||||||
@@ -25,8 +25,7 @@ interface AnalyticsType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AnalyticsDetailType extends AnalyticsType {
|
interface AnalyticsDetailType extends AnalyticsType {
|
||||||
detailNo: number
|
detailNo?: number
|
||||||
// analyticsNo in AnalyticsType
|
|
||||||
sbNo?: string
|
sbNo?: string
|
||||||
areaNo?: string
|
areaNo?: string
|
||||||
event?: string
|
event?: string
|
||||||
@@ -81,7 +80,6 @@ interface AnalyticsLogDataTracking {
|
|||||||
click_sarea: string
|
click_sarea: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
AnalyticsType,
|
AnalyticsType,
|
||||||
AnalyticsDetailType,
|
AnalyticsDetailType,
|
||||||
@@ -89,5 +87,5 @@ export type {
|
|||||||
AnalyticsLogDataTracking,
|
AnalyticsLogDataTracking,
|
||||||
// [API] Req / Res -----
|
// [API] Req / Res -----
|
||||||
ReqGetAnalytics,
|
ReqGetAnalytics,
|
||||||
ResGetAnalytics
|
ResGetAnalytics,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,3 +43,9 @@ export interface RequestObject {
|
|||||||
headers: Record<string, string>
|
headers: Record<string, string>
|
||||||
socket: { remoteAddress?: string }
|
socket: { remoteAddress?: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TrackingObject {
|
||||||
|
action_type: string
|
||||||
|
click_item: string
|
||||||
|
click_sarea: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { TrackingObject } from '#layers/types/api/common'
|
||||||
|
|
||||||
// API 요청 파라미터 타입
|
// API 요청 파라미터 타입
|
||||||
export interface GameDataRequest {
|
export interface GameDataRequest {
|
||||||
game_alias: string
|
game_alias: string
|
||||||
@@ -142,16 +144,9 @@ export interface GameDataResourceGroupBtnInfo {
|
|||||||
detail: Record<string, any>
|
detail: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
// 트래킹 타입
|
|
||||||
export interface GameDataTracking {
|
|
||||||
viewType: string
|
|
||||||
actionType: string
|
|
||||||
clickSarea: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GameDataResourceGroup {
|
export interface GameDataResourceGroup {
|
||||||
btn_info?: GameDataResourceGroupBtnInfo
|
btn_info?: GameDataResourceGroupBtnInfo
|
||||||
tracking: GameDataTracking
|
tracking: TrackingObject
|
||||||
}
|
}
|
||||||
|
|
||||||
type MarketPlatform = 'pc' | 'app_store' | 'google_play'
|
type MarketPlatform = 'pc' | 'app_store' | 'google_play'
|
||||||
@@ -181,7 +176,7 @@ export interface GameDataMenu {
|
|||||||
url_path: string
|
url_path: string
|
||||||
link_target: string
|
link_target: string
|
||||||
children: GameDataMenuChildren
|
children: GameDataMenuChildren
|
||||||
tracking_json: string | GameDataTracking // JSON 문자열 또는 객체로 변경
|
tracking_json: string | TrackingObject // JSON 문자열 또는 객체로 변경
|
||||||
}
|
}
|
||||||
|
|
||||||
// GNB 설정 타입
|
// GNB 설정 타입
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { TrackingObject } from '#layers/types/api/common'
|
||||||
|
|
||||||
// API 요청 파라미터 타입
|
// API 요청 파라미터 타입
|
||||||
export interface PageDataRequest {
|
export interface PageDataRequest {
|
||||||
game_alias: string
|
game_alias: string
|
||||||
@@ -51,7 +53,7 @@ export interface PageDataLnbMenu {
|
|||||||
menu_name: string
|
menu_name: string
|
||||||
target_type: number
|
target_type: number
|
||||||
page_ver_tmpl_name_en: string
|
page_ver_tmpl_name_en: string
|
||||||
tracking_json: Record<string, PageDataTracking>
|
tracking_json: Record<string, TrackingObject>
|
||||||
children?: Record<string, PageDataLnbMenu>
|
children?: Record<string, PageDataLnbMenu>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +95,7 @@ export interface PageDataResourceGroupBtnInfo {
|
|||||||
|
|
||||||
// 리소스 그룹 타입
|
// 리소스 그룹 타입
|
||||||
export interface PageDataResourceGroup {
|
export interface PageDataResourceGroup {
|
||||||
|
group_label?: string
|
||||||
resource_type?: PageDataResourceGroupType
|
resource_type?: PageDataResourceGroupType
|
||||||
res_path?: PageDataResourceGroupResPath
|
res_path?: PageDataResourceGroupResPath
|
||||||
btn_info?: PageDataResourceGroupBtnInfo
|
btn_info?: PageDataResourceGroupBtnInfo
|
||||||
@@ -101,7 +104,7 @@ export interface PageDataResourceGroup {
|
|||||||
color_code?: string
|
color_code?: string
|
||||||
color_name?: string
|
color_name?: string
|
||||||
}
|
}
|
||||||
tracking?: PageDataTracking
|
tracking?: TrackingObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PageDataResourceGroups = PageDataResourceGroup[]
|
export type PageDataResourceGroups = PageDataResourceGroup[]
|
||||||
@@ -171,15 +174,8 @@ export interface PageDataApiResult {
|
|||||||
data: PageDataResponse | null
|
data: PageDataResponse | null
|
||||||
error: string | null
|
error: string | null
|
||||||
}
|
}
|
||||||
// Tracking 타입
|
|
||||||
export interface PageDataTracking {
|
|
||||||
click_item: string
|
|
||||||
action_type: string
|
|
||||||
click_sarea: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrow 컴포넌트 타입
|
// Arrow 컴포넌트 타입
|
||||||
|
|
||||||
export type PageDataArrowComponent = PageDataTemplateComponent & {
|
export type PageDataArrowComponent = PageDataTemplateComponent & {
|
||||||
groups: PageDataResourceGroups
|
groups: PageDataResourceGroups
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export default {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
screens: {
|
screens: {
|
||||||
|
xxs: '0px', // 0px ~ 359px
|
||||||
xs: '360px', // Mobile: 360px ~ 767px
|
xs: '360px', // Mobile: 360px ~ 767px
|
||||||
sm: '768px', // Tablet: 768px ~ 1023px
|
sm: '768px', // Tablet: 768px ~ 1023px
|
||||||
md: '1024px', // PC: 1024px ~ 1439px
|
md: '1024px', // PC: 1024px ~ 1439px
|
||||||
|
|||||||
Reference in New Issue
Block a user