Merge branch 'all'
This commit is contained in:
@@ -18,7 +18,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
pagination: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['mounted', 'move', 'moved'])
|
||||
const emit = defineEmits(['mounted', 'move', 'moved', 'arrowClick'])
|
||||
|
||||
const isMultipleItems = computed(() => {
|
||||
return props.slideItemLength > 1
|
||||
@@ -82,6 +82,11 @@ const style = computed(() => {
|
||||
const handleSplideMounted = (splide: SplideType) => {
|
||||
emit('mounted', splide)
|
||||
splide.refresh()
|
||||
|
||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
||||
nextTick(() => {
|
||||
addArrowClickListeners(splide)
|
||||
})
|
||||
}
|
||||
|
||||
const handleMove = (
|
||||
@@ -101,6 +106,37 @@ const handleMoved = (
|
||||
) => {
|
||||
emit('moved', splide, newIndex, oldIndex, destIndex)
|
||||
}
|
||||
|
||||
const handleArrowClick = (direction: 'prev' | 'next', splide: SplideType) => {
|
||||
const currentIndex = splide.index
|
||||
const totalSlides = splide.length
|
||||
|
||||
// 이동할 슬라이드 인덱스 계산
|
||||
let targetIndex: number
|
||||
if (direction === 'next') {
|
||||
targetIndex = currentIndex + 1 >= totalSlides ? 0 : currentIndex + 1
|
||||
} else {
|
||||
targetIndex = currentIndex - 1 < 0 ? totalSlides - 1 : currentIndex - 1
|
||||
}
|
||||
|
||||
emit('arrowClick', direction, targetIndex)
|
||||
}
|
||||
|
||||
// 화살표 버튼에 클릭 이벤트 리스너 추가
|
||||
const addArrowClickListeners = (splide: SplideType) => {
|
||||
const prevArrow = splide.root.querySelector('.arrow-prev')
|
||||
const nextArrow = splide.root.querySelector('.arrow-next')
|
||||
|
||||
if (prevArrow) {
|
||||
prevArrow.addEventListener('click', () => handleArrowClick('prev', splide))
|
||||
}
|
||||
|
||||
if (nextArrow) {
|
||||
nextArrow.addEventListener('click', () => handleArrowClick('next', splide))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Splide } from '@splidejs/vue-splide'
|
||||
import type { ResponsiveOptions } from '@splidejs/splide'
|
||||
import type { Splide as SplideType, ResponsiveOptions } from '@splidejs/splide'
|
||||
|
||||
interface Props {
|
||||
autoplay?: boolean | string
|
||||
@@ -15,6 +15,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
pagination: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['mounted', 'move', 'moved', 'arrowClick'])
|
||||
|
||||
const splideRef = ref()
|
||||
|
||||
const options = computed((): ResponsiveOptions => {
|
||||
@@ -44,10 +46,74 @@ const options = computed((): ResponsiveOptions => {
|
||||
defineExpose({
|
||||
splide: computed(() => splideRef.value?.splide),
|
||||
})
|
||||
|
||||
const handleSplideMounted = (splide: SplideType) => {
|
||||
emit('mounted', splide)
|
||||
splide.refresh()
|
||||
|
||||
// 화살표 버튼 클릭 이벤트 리스너 추가
|
||||
nextTick(() => {
|
||||
addArrowClickListeners(splide)
|
||||
})
|
||||
}
|
||||
|
||||
const handleMove = (
|
||||
splide: SplideType,
|
||||
newIndex: number,
|
||||
oldIndex: number,
|
||||
destIndex: number
|
||||
) => {
|
||||
emit('move', splide, newIndex, oldIndex, destIndex)
|
||||
}
|
||||
|
||||
const handleMoved = (
|
||||
splide: SplideType,
|
||||
newIndex: number,
|
||||
oldIndex: number,
|
||||
destIndex: number
|
||||
) => {
|
||||
emit('moved', splide, newIndex, oldIndex, destIndex)
|
||||
}
|
||||
|
||||
const handleArrowClick = (direction: 'prev' | 'next', splide: SplideType) => {
|
||||
const currentIndex = splide.index
|
||||
const totalSlides = splide.length
|
||||
|
||||
// 이동할 슬라이드 인덱스 계산
|
||||
let targetIndex: number
|
||||
if (direction === 'next') {
|
||||
targetIndex = currentIndex + 1 >= totalSlides ? 0 : currentIndex + 1
|
||||
} else {
|
||||
targetIndex = currentIndex - 1 < 0 ? totalSlides - 1 : currentIndex - 1
|
||||
}
|
||||
|
||||
emit('arrowClick', direction, targetIndex)
|
||||
}
|
||||
|
||||
// 화살표 버튼에 클릭 이벤트 리스너 추가
|
||||
const addArrowClickListeners = (splide: SplideType) => {
|
||||
const prevArrow = splide.root.querySelector('.arrow-prev')
|
||||
const nextArrow = splide.root.querySelector('.arrow-next')
|
||||
|
||||
if (prevArrow) {
|
||||
prevArrow.addEventListener('click', () => handleArrowClick('prev', splide))
|
||||
}
|
||||
|
||||
if (nextArrow) {
|
||||
nextArrow.addEventListener('click', () => handleArrowClick('next', splide))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Splide ref="splideRef" :options="options" class="h-full">
|
||||
<Splide
|
||||
ref="splideRef"
|
||||
:options="options"
|
||||
class="h-full"
|
||||
@splide:mounted="handleSplideMounted"
|
||||
@splide:move="handleMove"
|
||||
@splide:moved="handleMoved"
|
||||
>
|
||||
<slot />
|
||||
</Splide>
|
||||
</template>
|
||||
|
||||
@@ -7,12 +7,12 @@ import type { ButtonType } from '#layers/types/components/button'
|
||||
|
||||
interface ButtonListProps {
|
||||
resourcesData: PageDataResourceGroup[]
|
||||
pageVerTmplSeq: number
|
||||
}
|
||||
|
||||
const props = defineProps<ButtonListProps>()
|
||||
|
||||
const { gameData } = useGameDataStore()
|
||||
|
||||
const BUTTON_TYPE_MAP = {
|
||||
URL: {
|
||||
_self: 'internal' as const,
|
||||
@@ -54,7 +54,12 @@ const getButtonBackgroundImage = (
|
||||
return ''
|
||||
}
|
||||
|
||||
const handleButtonClick = (btnInfo: PageDataResourceGroupBtnInfo) => {
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
|
||||
const handleButtonClick = (btnInfo: PageDataResourceGroupBtnInfo, index: any) => {
|
||||
sendLog(locale.value, useAnalyticsLogDataDirect(props.resourcesData[index], props.pageVerTmplSeq))
|
||||
|
||||
const marketType = btnInfo?.detail?.market_type
|
||||
if (marketType) {
|
||||
const url = gameData?.market[marketType]?.url
|
||||
@@ -93,9 +98,9 @@ const handleButtonClick = (btnInfo: PageDataResourceGroupBtnInfo) => {
|
||||
:style="{
|
||||
backgroundImage: `url(${getButtonBackgroundImage(button.btn_info)})`,
|
||||
}"
|
||||
@click="handleButtonClick(button.btn_info)"
|
||||
@click="handleButtonClick(button.btn_info, index)"
|
||||
>
|
||||
{{ button.btn_info?.txt_btn_name }}
|
||||
{{ button.btn_info?.txt_btn_name }}
|
||||
</AtomsButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -5,8 +5,8 @@ const props = defineProps<{
|
||||
resourcesData: PageDataResourceGroup
|
||||
pageVerTmplSeq: number
|
||||
}>()
|
||||
const { useAnalyticsLogData } = useAnalytics()
|
||||
const logData = useAnalyticsLogData(props.resourcesData, props.pageVerTmplSeq)
|
||||
const { useAnalyticsLogDataDirect } = useAnalytics()
|
||||
const logData = useAnalyticsLogDataDirect(props.resourcesData, props.pageVerTmplSeq)
|
||||
|
||||
// YouTube 모달 스토어 사용
|
||||
const modalStore = useModalStore()
|
||||
@@ -20,6 +20,7 @@ const handleVideoPlayClick = () => {
|
||||
|
||||
<template>
|
||||
<AtomsButtonPlay
|
||||
v-analytics="logData"
|
||||
:resources-data="resourcesData"
|
||||
@click="handleVideoPlayClick"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as amplitude from '@amplitude/analytics-browser'
|
||||
import type {
|
||||
AnalyticsDetailType,
|
||||
AnalyticsLogDataTracking,
|
||||
} from '../types/AnalyticsType'
|
||||
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
|
||||
import type { IdentityInfo, ActionInfo, MarketingInfo } from '../types/Stove'
|
||||
@@ -27,10 +26,8 @@ export const useAnalyticsLogData = (
|
||||
return ref({} as AnalyticsDetailType)
|
||||
}
|
||||
|
||||
const pageDataTrack = (
|
||||
typeof resourcesData.tracking === 'object' ? resourcesData.tracking : {}
|
||||
) as AnalyticsLogDataTracking
|
||||
console.log('🚀 ~ useAnalyticsLogData ~ pageDataTrack:', pageData)
|
||||
// const pageDataTrack = (typeof resourcesData.tracking === 'object' ? resourcesData.tracking : {}) as AnalyticsLogDataTracking
|
||||
const pageDataTrack = resourcesData.tracking
|
||||
|
||||
const logData = ref({
|
||||
actionType: pageDataTrack?.action_type,
|
||||
@@ -51,6 +48,44 @@ export const useAnalyticsLogData = (
|
||||
return logData
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 데이터와 템플릿 정보를 기반으로 분석용 로그 데이터를 생성하는 composable (직접 객체 반환)
|
||||
* @param resourcesData 페이지 리소스 데이터
|
||||
* @param pageVerTmplSeq 템플릿 시퀀스 번호
|
||||
* @returns 분석용 로그 데이터 객체 (ref 없이)
|
||||
*/
|
||||
export const useAnalyticsLogDataDirect = (
|
||||
resourcesData: PageDataResourceGroup,
|
||||
pageVerTmplSeq: number
|
||||
) => {
|
||||
const store = usePageDataStore()
|
||||
const pageData = store.pageData
|
||||
|
||||
if (!pageData) {
|
||||
return {} as AnalyticsDetailType
|
||||
}
|
||||
// const pageDataTrack = (typeof resourcesData.tracking === 'object' ? resourcesData.tracking : {}) as AnalyticsLogDataTracking
|
||||
const pageDataTrack = resourcesData.tracking
|
||||
|
||||
const logData = {
|
||||
actionType: pageDataTrack?.action_type,
|
||||
// logSourceType:pageDataTrack.logSourceType,
|
||||
// viewArea:pageDataTrack.viewArea,
|
||||
// viewType:pageDataTrack.viewType,
|
||||
clickArea:pageData.page_name_en,
|
||||
clickSarea: pageData.templates[pageVerTmplSeq].page_ver_tmpl_name_en,
|
||||
clickItem: `${pageData.templates[pageVerTmplSeq].page_ver_tmpl_name}_${pageDataTrack?.click_item}`,
|
||||
event: pageData.page_name,
|
||||
eventCategory: `${pageData.page_name}_${pageDataTrack?.click_item}`,
|
||||
template_code: pageData.templates[pageVerTmplSeq].template_code,
|
||||
page_ver_tmpl_name: pageData.templates[pageVerTmplSeq].page_ver_tmpl_name,
|
||||
page_ver_tmpl_name_en: pageData.templates[pageVerTmplSeq].page_ver_tmpl_name_en,
|
||||
} as unknown as AnalyticsDetailType
|
||||
|
||||
return logData
|
||||
}
|
||||
|
||||
|
||||
// target에 {XX1, XX2}와 같은 형태가 포함되어 있을 경우 options.clickItem으로부터 값 추출하여 세팅
|
||||
const findValueFromOption = (target: string, { options = {} }: any) => {
|
||||
if (target.includes('{') && target.includes('}')) {
|
||||
@@ -327,5 +362,5 @@ const sendMarketingScript = ({
|
||||
}
|
||||
|
||||
export default () => {
|
||||
return { sendGA, sendSA, sendLog, sendMarketingScript, useAnalyticsLogData }
|
||||
return { sendGA, sendSA, sendLog, sendMarketingScript, useAnalyticsLogData, useAnalyticsLogDataDirect }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export default defineNuxtRouteMiddleware(async (to, _from) => {
|
||||
page_url: pageUrl,
|
||||
_t: Date.now().toString(), // 캐시 무효화를 위한 타임스탬프
|
||||
}
|
||||
console.log('🚀 ~ queryParams:', queryParams)
|
||||
// console.log('🚀 ~ queryParams:', queryParams)
|
||||
|
||||
const response = (await commonFetch('GET', apiUrl, {
|
||||
query: queryParams,
|
||||
|
||||
@@ -12,6 +12,9 @@ interface Props {
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
|
||||
const slideThumbnailRef = ref<any>(null)
|
||||
const playingSlideIndex = ref<number | null>(null)
|
||||
|
||||
@@ -61,6 +64,14 @@ const stopVideo = () => {
|
||||
playingSlideIndex.value = null
|
||||
}
|
||||
|
||||
const onArrowClick = (direction, targetIndex) => {
|
||||
const logTraking =
|
||||
direction == 'prev'
|
||||
? getComponentGroupAry(props.components, 'arrow')?.groups[0]
|
||||
: getComponentGroupAry(props.components, 'arrow')?.groups[1]
|
||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTraking, 1))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const mainInst = slideThumbnailRef.value?.mainInst
|
||||
@@ -86,6 +97,7 @@ onMounted(() => {
|
||||
variant="media"
|
||||
:drag="false"
|
||||
class="mt-[24px] md:mt-[32px]"
|
||||
@arrow-click="onArrowClick"
|
||||
>
|
||||
<SplideSlide
|
||||
v-for="(item, index) in slideData"
|
||||
|
||||
@@ -54,6 +54,15 @@ const handleChange = (
|
||||
'buttonList'
|
||||
)
|
||||
}
|
||||
const { locale } = useI18n()
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
const onArrowClick = (direction, targetIndex) => {
|
||||
const logTraking =
|
||||
direction == 'prev'
|
||||
? getComponentGroupAry(props.components, 'arrow')?.groups[0]
|
||||
: getComponentGroupAry(props.components, 'arrow')?.groups[1]
|
||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTraking, 1))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -72,11 +81,13 @@ const handleChange = (
|
||||
:pagination="false"
|
||||
class="mt-[24px] md:mt-[48px]"
|
||||
@move="handleChange"
|
||||
@arrow-click="onArrowClick"
|
||||
>
|
||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||
<div class="slide-inner border-line mt-auto">
|
||||
<BlocksVisualContent
|
||||
:resources-data="getComponentGroup(item, 'imgList')"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
object-fit="cover"
|
||||
:alt="getComponentGroup(item, 'subTitle')?.display?.text"
|
||||
/>
|
||||
@@ -86,6 +97,7 @@ const handleChange = (
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
class="mt-[40px] md:mt-[56px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -102,6 +102,7 @@ const handleChange = (
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
class="mt-[32px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -44,17 +44,22 @@ const buttonListData = computed(() =>
|
||||
:resources-data="descriptionData"
|
||||
class="w-full max-w-[355px] md:max-w-[944px]"
|
||||
/>
|
||||
<WidgetsVideoPlay
|
||||
v-if="videoPlayData"
|
||||
:resources-data="videoPlayData"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
/>
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData.length > 0"
|
||||
:resources-data="buttonListData"
|
||||
button-type="market"
|
||||
class="mt-[22px] md:mt-[52px]"
|
||||
/>
|
||||
<client-only>
|
||||
<WidgetsVideoPlay
|
||||
v-if="videoPlayData"
|
||||
:resources-data="videoPlayData"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
/>
|
||||
</client-only>
|
||||
<client-only>
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData.length > 0"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
button-type="market"
|
||||
class="mt-[22px] md:mt-[52px]"
|
||||
/>
|
||||
</client-only>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -99,6 +99,7 @@ const slideItemSize = {
|
||||
<WidgetsButtonList
|
||||
v-if="buttonListData.length > 0"
|
||||
:resources-data="buttonListData"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
class="mt-[48px] md:mt-[72px]"
|
||||
/>
|
||||
<BlocksSlideCenterHighlight
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { SplideSlide } from '@splidejs/vue-splide'
|
||||
import {
|
||||
hasComponentGroup,
|
||||
getComponentGroup,
|
||||
getComponentContainer,
|
||||
getComponentGroupAry,
|
||||
getComponentGroup,
|
||||
hasComponentGroup,
|
||||
} from '#layers/utils/dataUtil'
|
||||
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
|
||||
|
||||
@@ -12,11 +13,23 @@ interface Props {
|
||||
pageVerTmplSeq: string
|
||||
}
|
||||
|
||||
const { locale } = useI18n()
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
|
||||
|
||||
const slideData = computed(() => {
|
||||
return getComponentContainer(props.components, 'group_sets')
|
||||
})
|
||||
|
||||
const onArrowClick = direction => {
|
||||
const logTraking =
|
||||
direction == 'prev'
|
||||
? getComponentGroupAry(props.components, 'arrow')?.groups[0]
|
||||
: getComponentGroupAry(props.components, 'arrow')?.groups[1]
|
||||
sendLog(locale.value, useAnalyticsLogDataDirect(logTraking, 1))
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -26,6 +39,7 @@ const slideData = computed(() => {
|
||||
:arrows="true"
|
||||
:pagination="true"
|
||||
class="h-full"
|
||||
@arrow-click="onArrowClick"
|
||||
>
|
||||
<SplideSlide v-for="(item, index) in slideData" :key="index">
|
||||
<WidgetsBackground
|
||||
@@ -51,6 +65,7 @@ const slideData = computed(() => {
|
||||
<WidgetsButtonList
|
||||
v-if="hasComponentGroup(item, 'buttonList')"
|
||||
:resources-data="getComponentGroupAry(item, 'buttonList')"
|
||||
:page-ver-tmpl-seq="Number(props.pageVerTmplSeq)"
|
||||
class="mt-[28px] md:mt-[52px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@ export interface PageDataLnbMenu {
|
||||
menu_name: string
|
||||
target_type: number
|
||||
page_ver_tmpl_name_en: string
|
||||
tracking: string
|
||||
// tracking: Record<string, PageDataTracking>
|
||||
}
|
||||
|
||||
// 메타 태그 타입
|
||||
@@ -98,7 +98,7 @@ export interface PageDataResourceGroup {
|
||||
color_code?: string
|
||||
color_name?: string
|
||||
}
|
||||
tracking: string
|
||||
tracking: PageDataTracking
|
||||
}
|
||||
|
||||
export type PageDataResourceGroups = PageDataResourceGroup[]
|
||||
@@ -133,6 +133,13 @@ export interface PageDataTemplate {
|
||||
components: PageDataTemplateComponents
|
||||
}
|
||||
|
||||
// Tracking 타입
|
||||
export interface PageDataTracking {
|
||||
click_item: string
|
||||
action_type: string
|
||||
click_sarea: string
|
||||
}
|
||||
|
||||
// ===== API 관련 타입들 =====
|
||||
|
||||
// API 에러 응답 타입
|
||||
|
||||
Reference in New Issue
Block a user