fix. [QA] 액션 버튼 동작 이슈 수정 (PWT-53, PWT-67)

This commit is contained in:
clkim
2025-11-25 18:56:02 +09:00
parent 319d299cfe
commit f98c7051f3
4 changed files with 106 additions and 49 deletions

View File

@@ -6,7 +6,6 @@ interface props {
buttonSize?: string
target?: '_self' | '_blank'
href?: string
rel?: string
backgroundColor?: string
textColor?: string
disabled?: boolean
@@ -24,7 +23,6 @@ const props = withDefaults(defineProps<props>(), {
const componentTag = computed((): string => {
switch (props.type) {
case 'external':
case 'download':
case 'link':
return 'a'
case 'internal':
@@ -34,29 +32,20 @@ const componentTag = computed((): string => {
}
})
const componentProps = computed(() => {
const baseProps = { disabled: props.disabled }
if (
props.type === 'external' ||
props.type === 'download' ||
props.type === 'link'
) {
if (props.type === 'external' || props.type === 'link') {
return {
...baseProps,
href: props.href,
target: props.target,
rel: props.rel,
}
}
if (props.type === 'internal') {
return {
...baseProps,
to: props.href,
}
}
return baseProps
return {}
})
</script>
@@ -64,12 +53,13 @@ const componentProps = computed(() => {
<component
:is="componentTag"
v-bind="{ ...componentProps }"
:class="['btn-base', props.buttonSize]"
:class="['btn-base', props.buttonSize, { disabled: props.disabled }]"
:style="{
backgroundColor: props.backgroundColor,
color: props.textColor,
'--text-color': props.textColor,
}"
:disabled="props.disabled"
>
<span class="btn-content">
<slot />
@@ -101,12 +91,12 @@ const componentProps = computed(() => {
.btn-base:hover {
@apply after:opacity-20;
}
.btn-base:disabled {
@apply cursor-default
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
.btn-base.disabled {
@apply cursor-default pointer-events-none
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
}
.btn-base:disabled .btn-content {
.btn-base.disabled .btn-content {
@apply opacity-50;
}
.btn-base .btn-content {

View File

@@ -1,14 +1,14 @@
<script setup lang="ts">
import type { PageDataLnbMenu } from '#layers/types/api/pageData'
const { y: windowY, directions } = useWindowScroll({ behavior: 'smooth' })
const { directions } = useWindowScroll({ behavior: 'smooth' })
const pageDataStore = usePageDataStore()
const scrollStore = useScrollStore()
const breakpoints = useResponsiveBreakpoints()
const { pageData } = storeToRefs(pageDataStore)
// 상수 정의
const HEADER_HEIGHT = 64
const OBSERVER_OPTIONS = {
root: null,
rootMargin: '-20% 0px -60% 0px', // 상단 20%, 하단 60% 마진
@@ -104,15 +104,7 @@ const handleLnbClick = (lnbItem: PageDataLnbMenu) => {
? lnbItem?.children?.['1']?.page_ver_tmpl_name_en
: lnbItem.page_ver_tmpl_name_en
const targetElement = document.getElementById(targetId)
if (!targetElement) return
// 헤더 높이를 고려한 스크롤 위치 계산
const elementTop = targetElement.getBoundingClientRect().top
const currentScrollY = window.scrollY
const targetScrollY = currentScrollY + elementTop - HEADER_HEIGHT
windowY.value = targetScrollY
scrollStore.scrollToAnchor(targetId)
}
watch(directions, newVal => {

View File

@@ -9,33 +9,93 @@ interface Props {
resourcesData: PageDataResourceGroup[]
pageVerTmplSeq: number
}
const props = defineProps<Props>()
const { locale } = useI18n()
const modalStore = useModalStore()
const scrollStore = useScrollStore()
const breakpoints = useResponsiveBreakpoints()
const { sendLog, useAnalyticsLogDataDirect } = useAnalytics()
const { tm } = useI18n()
const getBtnType = (item?: PageDataResourceGroupBtnInfo): ButtonType => {
const type = item?.detail?.btn_type
const target = item?.detail?.action?.link_target
if (type === 'URL' && target)
const buttonList = computed<PageDataResourceGroup[]>(
() => props.resourcesData ?? []
)
const getButtonType = (btnInfo?: PageDataResourceGroupBtnInfo): ButtonType => {
const btnType = btnInfo?.detail?.btn_type
const target = btnInfo?.detail?.action?.link_target
if (btnType === 'URL' && target) {
return target === '_blank' ? 'external' : 'internal'
if (type === 'DOWNLOAD') return 'download'
}
if (btnType === 'DOWNLOAD') return 'download'
return 'action'
}
const handleLogClick = (button: PageDataResourceGroup) => {
sendLog(locale.value, useAnalyticsLogDataDirect(button, props.pageVerTmplSeq))
if (button.btn_info?.detail?.btn_type === 'POP') {
modalStore.handleOpenContent({
contentTitle: button.btn_info?.detail?.title,
tabInfo: button.btn_info?.detail?.tab_info,
const downloadZip = async (url: string, osType: number) => {
if (osType === 1 && breakpoints.value?.isMobile) {
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_PC') })
return
}
const fileUrl = getResourceHost(url)
try {
const res = await $fetch(fileUrl, {
method: 'GET',
responseType: 'blob',
timeout: 10000,
})
const blob = new Blob([res as BlobPart], { type: 'application/zip' })
const blobUrl = URL.createObjectURL(blob)
const pathPart = fileUrl.split('/').pop()
const a = document.createElement('a')
a.href = blobUrl
a.download = pathPart ?? 'download.zip'
a.click()
// 메모리 정리
URL.revokeObjectURL(blobUrl)
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_Success') })
} catch (err) {
console.error(err)
modalStore.handleOpenAlert({ contentText: tm('Alert_Download_Error') })
}
}
// 편의상
const buttonList = computed(() => props.resourcesData || [])
const handleButtonClick = (button: PageDataResourceGroup) => {
// 로그
sendLog(locale.value, useAnalyticsLogDataDirect(button, props.pageVerTmplSeq))
const btnDetail = button.btn_info?.detail
switch (btnDetail?.btn_type) {
case 'POP':
modalStore.handleOpenContent({
contentTitle: btnDetail?.title,
tabInfo: btnDetail?.tab_info,
})
return
case 'ANCHOR':
scrollStore.scrollToAnchor(btnDetail?.page_ver_tmpl_name_en ?? '')
return
case 'MOV':
modalStore.handleOpenYoutube({ youtubeUrl: btnDetail.url ?? '' })
return
case 'DOWNLOAD':
downloadZip(btnDetail?.file_path ?? '', btnDetail?.os_type ?? 0)
return
default:
return
}
}
</script>
<template>
@@ -50,21 +110,20 @@ const buttonList = computed(() => props.resourcesData || [])
:platform="button.btn_info?.detail?.market_type"
:background-color="getColorCodeFromData(button.btn_info, 'btn')"
:text-color="getColorCodeFromData(button.btn_info, 'txt')"
:disabled="button?.btn_info?.disabled"
@click="handleLogClick(button)"
@click="handleButtonClick(button)"
>
{{ button.btn_info?.txt_btn_name }}
</BlocksButtonLauncher>
<AtomsButton
v-else
:type="getBtnType(button.btn_info)"
:type="getButtonType(button.btn_info)"
:href="button.btn_info?.detail?.action?.url"
:target="button.btn_info?.detail?.action?.link_target"
:rel="button.btn_info?.detail?.action?.rel"
:background-color="getColorCodeFromData(button.btn_info, 'btn')"
:text-color="getColorCodeFromData(button.btn_info, 'txt')"
:disabled="button?.btn_info?.disabled"
@click="handleLogClick(button)"
:disabled="button.btn_info?.detail?.btn_type === 'DEACTIVE'"
@click="handleButtonClick(button)"
>
{{ button.btn_info?.txt_btn_name }}
</AtomsButton>

View File

@@ -3,7 +3,11 @@ import { useWindowScroll } from '@vueuse/core'
export const useScrollStore = defineStore('scrollStore', () => {
const { y: windowY } = useWindowScroll({ behavior: 'smooth' })
const breakpoints = useResponsiveBreakpoints()
const headerHeight = computed(() => {
return breakpoints.value.isMobile ? 48 : 64
})
const stoveGnbHeight = 48 as number
const isPassedStoveGnb = ref(false)
const scrollGnbPosition = ref(stoveGnbHeight)
@@ -24,7 +28,7 @@ export const useScrollStore = defineStore('scrollStore', () => {
const controlScrollLock = (state: boolean) => {
if (!import.meta.client) return
if (state) {
document.body.classList.add('scroll-lock')
} else {
@@ -32,6 +36,17 @@ export const useScrollStore = defineStore('scrollStore', () => {
}
}
const scrollToAnchor = (targetId: string) => {
const targetElement = document.getElementById(targetId)
if (!targetElement) return
const elementTop = targetElement.getBoundingClientRect().top
const currentScrollY = window.scrollY
const targetScrollY = currentScrollY + elementTop - headerHeight.value
windowY.value = targetScrollY
}
return {
stoveGnbHeight,
isPassedStoveGnb,
@@ -39,5 +54,6 @@ export const useScrollStore = defineStore('scrollStore', () => {
updateScrollValue,
controlScrollLock,
scrollToAnchor,
}
})