fix. 런처실행 버튼 수정

This commit is contained in:
clkim
2025-10-29 17:55:14 +09:00
parent 6482c7f832
commit ffac118378
14 changed files with 183 additions and 176 deletions

View File

@@ -146,14 +146,14 @@ onBeforeUnmount(() => {
<NuxtPage />
<!-- 공통 모달 컴포넌트 -->
<BlocksModalYouTube
<AtomsModalYouTube
v-model:is-open="youtube.storeIsOpen"
:youtube-url="youtube.storeYoutubeUrl"
:is-outside-close="youtube.storeIsOutsideClose"
:modal-name="youtube.storeModalName"
@close-button-event="handleResetYoutube"
/>
<BlocksModalConfirm
<AtomsModalConfirm
v-model:is-open="confirm.storeIsOpen"
:is-show-dimmed="confirm.storeIsShowDimmed"
:content-text="confirm.storeContentText"
@@ -164,7 +164,7 @@ onBeforeUnmount(() => {
@confirm-button-event="confirm.storeConfirmButtonEvent"
@cancel-button-event="confirm.storeCancelButtonEvent"
/>
<BlocksModalAlert
<AtomsModalAlert
v-model:is-open="alert.storeIsOpen"
:is-show-dimmed="alert.storeIsShowDimmed"
:content-text="alert.storeContentText"
@@ -175,8 +175,8 @@ onBeforeUnmount(() => {
/>
<!-- 로딩 컴포넌트 -->
<BlocksLoadingFull />
<BlocksLoadingLocal />
<AtomsLoadingFull />
<AtomsLoadingLocal />
</template>
<style>

View File

@@ -22,6 +22,10 @@ const props = withDefaults(defineProps<Props>(), {
disabled: false,
})
const runtimeConfig = useRuntimeConfig()
const { gameData } = useGameDataStore()
const { isProcessing, validateLauncher } = useCheckGameStart()
const PLATFORM_ICON_MAP: Record<Platform, string> = {
google_play: 'AtomsIconsLogoGoogle',
app_store: 'AtomsIconsLogoApple',
@@ -36,9 +40,15 @@ const DUP_IMAGE_MAP: Record<Platform, string> = {
stove: '/images/common/btn_system_normal_stove_pc.svg',
} as const
const componentTag = computed(() => {
if (props.platform === 'stove') {
return 'a'
}
return 'button'
})
const isDuplication = computed(() => props.type === 'duplication')
const isSingle = computed(() => props.type === 'single')
const isCustom = computed(() => props.type === 'custom')
const platformIcon = computed(() => PLATFORM_ICON_MAP[props.platform])
const duplicationImage = computed(() =>
isDuplication.value ? DUP_IMAGE_MAP[props.platform] : ''
@@ -58,11 +68,27 @@ const inlineStyle = computed<CSSProperties>(() => {
}
return style
})
const handleClick = () => {
if (props.platform === 'pc') {
validateLauncher()
return
}
if (props.platform === 'stove') {
const stoveClientDownloadUrl = runtimeConfig.public.stoveClientDownloadUrl
location.href = stoveClientDownloadUrl
return
}
const url = gameData?.market_json[props.platform]?.url || ''
window.open(url, '_blank')
}
</script>
<template>
<button
type="button"
<component
:is="componentTag"
v-bind="$attrs"
:class="[
'btn-base',
props.type,
@@ -71,12 +97,13 @@ const inlineStyle = computed<CSSProperties>(() => {
:data-variant="props.variant"
:data-platform="props.platform"
:style="inlineStyle"
:disabled="disabled"
:disabled="disabled || isProcessing"
@click="handleClick"
>
<span class="btn-content">
<component
:is="platformIcon"
v-if="!isDuplication"
v-if="!isDuplication && !isCustom"
class="icon-platform"
/>
<span class="text">
@@ -86,12 +113,18 @@ const inlineStyle = computed<CSSProperties>(() => {
<AtomsIconsDownloadLine />
</span>
</span>
</button>
</component>
<ClientOnly>
<Teleport to="#teleports">
<BlocksModalClient />
</Teleport>
</ClientOnly>
</template>
<style scoped>
.btn-base {
@apply overflow-hidden relative font-medium cursor-pointer
@apply overflow-hidden inline-block relative font-medium cursor-pointer
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0;
}
@@ -146,10 +179,8 @@ const inlineStyle = computed<CSSProperties>(() => {
.btn-base.duplication {
@apply items-start bg-[16px_50%] bg-[length:auto_28px] bg-no-repeat rounded-[4px]
before:rounded-[4px] after:rounded-[4px]
pt-[22px] pl-[44px] pr-[22px] pb-[7px] text-[11px]
pt-[22px] pl-[47px] pr-[22px] pb-[7px] text-[11px]
md:h-[64px] md:pt-[30px] md:pl-[64px] md:pr-[28px] md:pb-[11px] md:text-[12px] md:bg-[20px_50%] md:bg-[length:auto_40px];
color: red;
}
.btn-base.duplication[data-platform='google_play'] {
@apply min-w-[148px] md:min-w-[194px];

View File

@@ -1,87 +0,0 @@
<script setup>
const {
isProcessing,
isShowCheckLauncher,
isShowDownloadLauncher,
validateLauncher,
downloadLauncher,
} = useCheckGameStart()
</script>
<template>
<AtomsButton
button-size="size-small md:size-small"
:class="$attrs?.class"
:disabled="isProcessing"
style="font-size: 16px"
@click="validateLauncher"
>
<slot />
</AtomsButton>
<ClientOnly>
<Teleport to="#teleports">
<BlocksModalLayer
v-model:is-open="isShowCheckLauncher"
:is-show-dimmed="true"
:is-outside-close="false"
:modal-name="'launcher'"
area-class="max-w-[480px] pt-[56px] px-[24px] pb-[24px] rounded-[8px]"
close-class="absolute top-[16px] right-[24px]"
>
<span class="ico-loading"></span>
<!-- [TODO] i18n 적용 -->
<!-- <p class="text-check">{{ tm('Common_Message_Check_Client').txt }}</p> -->
<p class="text-check">pc 클라이언트 실행 ...</p>
<Transition name="fade">
<div v-if="isShowDownloadLauncher" class="client-area">
<!-- <p
v-dompurify-html="tm('Common_Message_Download_Client').txt"
class="text-info"
></p>
<button type="button" class="btn-download" @click="downloadLauncher">
{{ tm('Common_Message_Install').txt }}
</button>
<p
v-dompurify-html="tm('Common_Message_Download_Close').txt"
class="text-tip"
></p> -->
<p class="text-info">
PC 클라이언트가 실행되지 않나요?
<br />
다운로드 전이라면 다운로드 진행해주세요
</p>
<AtomsButtonVariant class="max-w-[300px]" @click="downloadLauncher">
다운로드
</AtomsButtonVariant>
<p
v-dompurify-html="
'*PC 클라이언트가 정상 실행되었다면 팝업을 닫아 주세요.'
"
class="text-tip"
></p>
</div>
</Transition>
</BlocksModalLayer>
</Teleport>
</ClientOnly>
</template>
<style scoped>
.ico-loading {
@apply block mx-auto mb-4 w-[80px] h-[80px] bg-[url('/images/common/stove_loading_light.png')] bg-contain bg-center bg-no-repeat;
}
.text-check {
@apply mb-6 text-center text-[20px] font-bold leading-[30px] tracking-[-0.6px] text-[#333333];
}
.client-area {
@apply pt-4 border-t border-[rgba(0,0,0,0.08)] text-center;
}
.text-info {
@apply mb-3 text-[14px] font-medium leading-[24px] tracking-[-0.42px] text-[#333333];
}
.text-tip {
@apply mt-4 text-[14px] leading-[20px] tracking-[-0.42px] text-[#999999];
}
</style>

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
const { isShowCheckLauncher, isShowDownloadLauncher, downloadLauncher } =
useCheckGameStart()
</script>
<template>
<AtomsModalLayer
v-model:is-open="isShowCheckLauncher"
:is-show-dimmed="true"
:is-outside-close="false"
:modal-name="'launcher'"
area-class="max-w-[480px] pt-[56px] px-[24px] pb-[24px] rounded-[8px]"
close-class="absolute top-[16px] right-[24px]"
>
<span class="ico-loading"></span>
<!-- [TODO] i18n 적용 -->
<!-- <p class="text-check">{{ tm('Common_Message_Check_Client').txt }}</p> -->
<p class="text-check">pc 클라이언트 실행 ...</p>
<Transition name="fade">
<div v-if="isShowDownloadLauncher" class="client-area">
<!-- <p
v-dompurify-html="tm('Common_Message_Download_Client').txt"
class="text-info"
></p>
<button type="button" class="btn-download" @click="downloadLauncher">
{{ tm('Common_Message_Install').txt }}
</button>
<p
v-dompurify-html="tm('Common_Message_Download_Close').txt"
class="text-tip"
></p> -->
<p class="text-info">
PC 클라이언트가 실행되지 않나요?
<br />
다운로드 전이라면 다운로드 진행해주세요
</p>
<AtomsButtonVariant class="max-w-[300px]" @click="downloadLauncher">
다운로드
</AtomsButtonVariant>
<p
v-dompurify-html="
'*PC 클라이언트가 정상 실행되었다면 팝업을 닫아 주세요.'
"
class="text-tip"
></p>
</div>
</Transition>
</AtomsModalLayer>
</template>
<style scoped>
.ico-loading {
@apply block mx-auto mb-4 w-[80px] h-[80px] bg-[url('/images/common/stove_loading_light.png')] bg-contain bg-center bg-no-repeat;
}
.text-check {
@apply mb-6 text-center text-[20px] font-bold leading-[30px] tracking-[-0.6px] text-[#333333];
}
.client-area {
@apply pt-4 border-t border-[rgba(0,0,0,0.08)] text-center;
}
.text-info {
@apply mb-3 text-[14px] font-medium leading-[24px] tracking-[-0.42px] text-[#333333];
}
.text-tip {
@apply mt-4 text-[14px] leading-[20px] tracking-[-0.42px] text-[#999999];
}
</style>

View File

@@ -296,9 +296,14 @@ onBeforeUnmount(() => {
</div>
</nav>
<div ref="startRef" class="btn-start">
<BlocksButtonLuncher class="w-full md:w-auto">
<BlocksButtonDownload
type="custom"
platform="pc"
class="w-full md:w-auto"
style="font-size: 16px"
>
게임 시작
</BlocksButtonLuncher>
</BlocksButtonDownload>
</div>
<button class="btn-close" @click="handleMenuClose">
<AtomsIconsMenuCloseLine class="mx-auto" />

View File

@@ -12,7 +12,6 @@ interface ButtonListProps {
const props = defineProps<ButtonListProps>()
const { gameData } = useGameDataStore()
const { locale } = useI18n()
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
@@ -40,38 +39,11 @@ const getButtonType = (btnInfo: PageDataResourceGroupBtnInfo): ButtonType => {
return DEFAULT_BUTTON_TYPE
}
const getButtonBackgroundImage = (
btnInfo: PageDataResourceGroupBtnInfo
): string => {
const marketType = btnInfo?.detail?.market_type
const marketImageMap: Record<string, string> = {
google_play: '/images/common/btn_logo-google.svg',
app_store: '/images/common/btn_logo-app.svg',
pc: '/images/common/btn_logo-pc.svg',
}
if (marketType && marketImageMap[marketType]) {
return marketImageMap[marketType]
}
return ''
}
const handleButtonClick = (
btnInfo: PageDataResourceGroupBtnInfo,
index: any
) => {
const handleSendLog = (index: number) => {
sendLog(
locale.value,
useAnalyticsLogDataDirect(props.resourcesData[index], props.pageVerTmplSeq)
)
const marketType = btnInfo?.detail?.market_type
if (marketType) {
const url = gameData?.market_json[marketType]?.url
window.open(url, '_blank')
return
}
}
</script>
@@ -80,41 +52,29 @@ const handleButtonClick = (
v-if="props.resourcesData?.length"
class="flex flex-wrap justify-center gap-3 md:gap-4"
>
<AtomsButton
v-for="(button, index) in props.resourcesData"
:key="index"
:type="getButtonType(button.btn_info)"
:target="button.btn_info?.detail?.action?.link_target"
:href="button.btn_info?.detail?.action?.url"
:rel="button.btn_info?.detail?.action?.rel"
:background-color="
getColorCode({
colorName: button.btn_info?.color_name_btn,
colorCode: button.btn_info?.color_code_btn,
})
"
:text-color="
getColorCode({
colorName: button.btn_info?.color_name_txt,
colorCode: button.btn_info?.color_code_txt,
})
"
:disabled="button.btn_info?.disabled"
:class="button.btn_info?.detail?.market_type ? 'btn-market' : ''"
:style="{
backgroundImage: `url(${getButtonBackgroundImage(button.btn_info)})`,
}"
@click="handleButtonClick(button.btn_info, index)"
>
{{ button.btn_info?.txt_btn_name }}
</AtomsButton>
<template v-for="(button, index) in props.resourcesData" :key="index">
<AtomsButton
:type="getButtonType(button.btn_info)"
:target="button.btn_info?.detail?.action?.link_target"
:href="button.btn_info?.detail?.action?.url"
:rel="button.btn_info?.detail?.action?.rel"
:background-color="
getColorCode({
colorName: button.btn_info?.color_name_btn,
colorCode: button.btn_info?.color_code_btn,
})
"
:text-color="
getColorCode({
colorName: button.btn_info?.color_name_txt,
colorCode: button.btn_info?.color_code_txt,
})
"
:disabled="button.btn_info?.disabled"
@click="handleSendLog(index)"
>
{{ button.btn_info?.txt_btn_name }}
</AtomsButton>
</template>
</div>
</template>
<style scoped>
:deep(.btn-market) {
@apply flex items-start bg-[16px_50%] bg-[length:auto_28px] bg-no-repeat
min-w-[113px] pt-[22px] pl-[44px] pr-[22px] text-[11px]
md:min-w-[150px] md:pt-[30px] md:pl-[64px] md:pr-[28px] md:text-[12px] md:bg-[20px_50%] md:bg-[length:auto_40px];
}
</style>

View File

@@ -9,6 +9,9 @@ interface Props {
const props = defineProps<Props>()
const { locale } = useI18n()
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
const backgroundData = computed(() =>
getComponentGroup(props.components, 'background')
)
@@ -24,6 +27,13 @@ const videoPlayData = computed(() =>
const buttonListData = computed(() =>
getComponentGroupAry(props.components, 'buttonList')
)
const handleSendLog = (index: number) => {
sendLog(
locale.value,
useAnalyticsLogDataDirect(buttonListData.value[index], props.pageVerTmplSeq)
)
}
</script>
<template>
@@ -49,13 +59,33 @@ const buttonListData = computed(() =>
:resources-data="videoPlayData"
:page-ver-tmpl-seq="props.pageVerTmplSeq"
/>
<WidgetsButtonList
<div
v-if="buttonListData.length > 0"
button-type="market"
:resources-data="buttonListData"
:page-ver-tmpl-seq="props.pageVerTmplSeq"
class="mt-[22px] md:mt-[52px]"
/>
class="flex flex-wrap justify-center gap-3 mt-[22px] md:gap-4 md:mt-[52px]"
>
<BlocksButtonDownload
v-for="(button, index) in buttonListData"
:key="index"
type="duplication"
:platform="button.btn_info?.detail?.market_type"
:background-color="
getColorCode({
colorName: button.btn_info?.color_name_btn,
colorCode: button.btn_info?.color_code_btn,
})
"
:text-color="
getColorCode({
colorName: button.btn_info?.color_name_txt,
colorCode: button.btn_info?.color_code_txt,
})
"
:disabled="button.btn_info?.disabled"
@click="handleSendLog(index)"
>
{{ button.btn_info?.txt_btn_name }}
</BlocksButtonDownload>
</div>
</div>
</section>
</template>

View File

@@ -5,7 +5,7 @@ export type ButtonType =
| 'action'
| 'link'
export type DownloadButtonType = 'default' | 'single' | 'duplication'
export type DownloadButtonType = 'default' | 'single' | 'duplication' | 'custom'
export type ButtonSize = 'large' | 'medium' | 'small' | 'extra-small'