Files
web-temp/layers/templates/FxSecure01/index.vue

413 lines
15 KiB
Vue

<script setup lang="ts">
import { getComponentGroup } from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
// Props
interface Props {
id?: string
components: PageDataTemplateComponents
pageVerTmplSeq: number
}
const props = defineProps<Props>()
const { handleTokenValidation } = useTokenValidation()
// Configuration
const runtimeConfig = useRuntimeConfig()
const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string
const stoveApiBaseUrl = runtimeConfig.public.stoveApiUrl
const multilingualFileName = 'STOVE_PUBTEMPLATE_homepage_brand_secure.json'
// Multilingual
const resultGetMultilingual = await useGetMultilingual({
baseApiUrl: dataResourcesUrl,
fileName: multilingualFileName,
})
const { tm, locale }: any = useI18n({
useScope: 'local',
messages: Object(resultGetMultilingual?.value?.multilingual),
})
const isLogin = ref(false)
const secureSetting = ref({
otpLoginYn: 'N',
abroadLoginBlockYn: 'N',
pcRegisterYn: 'N',
})
// 회원 보안 설정 설정
const handleSecureSetting = (url: string) => {
window.open(url, '_blank')
}
// 로그인 유효성 체크
const checkLoginValidation = async () => {
const accessToken = useCookie('SUAT')
const validateTokenResult = await handleTokenValidation(
accessToken.value || ''
)
isLogin.value = validateTokenResult
}
// 회원 보안 설정 조회
const fnGetSecuritySetting = async () => {
const accessToken = useCookie('SUAT')
checkLoginValidation()
const apiBase = `${stoveApiBaseUrl}/auth-secure/v1.0`
const headers = {
Authorization: `Bearer ${accessToken.value}`,
'Content-Type': 'application/json;charset=UTF-8',
}
try {
const result = await commonFetch('GET', `${apiBase}/security/setting`, {
headers,
loading: true,
})
if (result?.code === 0 && Array.isArray(result.value)) {
const arrSecure = result.value
const getValue = (key: string) =>
arrSecure.find((f: any) => f.key === key)?.value ?? 'N'
secureSetting.value = {
otpLoginYn: getValue('OTP_LOGIN_YN'),
abroadLoginBlockYn: getValue('ABROAD_LOGIN_BLOCK_YN'),
pcRegisterYn: getValue('PC_REGISTER_YN'),
}
}
} catch (e) {
console.error(e)
}
}
// Data
const backgroundData = computed(() =>
getComponentGroup(props.components, 'background')
)
// Computed
const secureCards = computed(() => {
const allCards = [
{
id: 'SECURE_CARD_0',
title: tm('Secure_Stove_otp') || '스토브 인증기 (OTP)',
description:
tm('Secure_Stove_otp_desc') ||
'스토브 앱으로 인증 후 안전하게 로그인하세요.',
status: secureSetting.value.otpLoginYn,
benefitTitle: tm('Secure_Stove_otp_benefits') || '스토브 OTP 혜택',
benefitDesc: tm('Secure_Defense_bonus_10') || '방어력 +10',
benefitIcon: '/images/common/img_OTP.png',
buttonDisabled: false,
url: tm('Secure_OtpLogin_Url'),
},
{
id: 'SECURE_CARD_1',
title: tm('Secure_Block_foreign_login') || '해외 로그인 차단',
description:
tm('Secure_Block_foreign_login_desc') ||
'접속 국가를 제한하여 의심 로그인을 차단해요.',
status: secureSetting.value.abroadLoginBlockYn,
benefitTitle: '',
benefitDesc: '',
benefitIcon: '',
buttonDisabled: false,
url: tm('Secure_AbroadLogin_Url'),
},
{
id: 'SECURE_CARD_2',
title: tm('Secure_Trusted_pc_management') || '지정 PC 관리',
description:
tm('Secure_Trusted_pc_desc') ||
'지정 PC에서만 로그인할 수 있게 설정해 보세요.',
status: secureSetting.value.pcRegisterYn,
benefitTitle: '',
benefitDesc: '',
benefitIcon: '',
buttonDisabled: false,
url: tm('Secure_PcRegister_Url'),
},
]
// 한국어일 때는 모든 카드 노출, 그 외 언어일 때는 SECURE_CARD_0만 노출
if (locale.value === 'ko') {
return allCards
}
return allCards.filter(card => card.id === 'SECURE_CARD_0')
})
// 유의사항 내용 다국어 조회
const cautionText = computed(() => {
return tm('Secure_Notice_Content') || []
})
onMounted(() => {
fnGetSecuritySetting()
})
</script>
<template>
<WidgetsFixMainTitle
:id="props.id"
:title="tm('Secure_Page_Title') || '보안 강화 캠페인'"
:resources-data="backgroundData"
class="mx-auto"
/>
<div class="section-container static">
<section class="section-secure bg-[#F0F0F0] pb-50">
<div class="section-static content-standa flex-wrap md:max-w-[1300px] mx-auto">
<!-- Title Section (Non-Korean only) -->
<div
v-if="locale !== 'ko'"
class="flex flex-col md:flex-row md:items-end md:justify-between gap-2 md:gap-5 mb-4 md:mb-6"
>
<h3
class="text-[#1F1F1F] text-[18px] md:text-[24px] font-bold leading-[26px] md:leading-[34px] tracking-[-0.54px] md:tracking-[-0.72px]"
>
{{ tm('Secure_Page_Title') || 'Security Settings' }}
</h3>
<p
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.' }}
</p>
</div>
<!-- Content Wrapper (Non-Korean: row layout with card and caution) -->
<div
:class="[
'w-full',
locale === 'ko' ? '' : 'flex flex-col md:flex-row gap-5 md:gap-6',
]"
>
<!-- Secure Cards -->
<div
:class="[
locale === 'ko'
? 'grid grid-cols-1 md:grid-cols-3 w-full gap-3 md:gap-5 mb-6'
: 'w-full md:w-auto md:flex-shrink-0',
]"
>
<div
v-for="card in secureCards"
:key="card.id"
:class="[
'bg-[#FFFFFF] rounded-2xl flex flex-col transition-all duration-300 ease-in-out',
locale === 'ko'
? 'flex-1 min-h-[308px] md:min-h-[384px] p-[10px] md:p-3 lg:p-4 gap-3'
: 'w-full md:w-[420px] h-auto md:h-[392px] p-[10px] md:p-4 gap-3',
]"
>
<!-- Card Content -->
<div
:class="[
'flex flex-col text-left',
locale === 'ko'
? 'flex-1 p-[10px] md:p-3 lg:p-4 gap-[8px] md:gap-3'
: 'flex-1 p-[10px] md:p-4 gap-2 md:gap-3',
]"
>
<!-- Badge -->
<div class="inline-flex">
<span
:class="[
locale === 'ko'
? 'px-1.5 md:px-2 py-0.5 md:py-1 rounded-full text-[12px] md:text-[14px] font-medium leading-5'
: 'px-[6px] md:px-2 py-[2px] md:py-1 rounded-full text-[12px] md:text-[14px] font-medium leading-[18px] md:leading-5',
card.status === 'Y'
? 'bg-[#E2EAFF] text-[#3C75FF]'
: 'bg-[#EBEBEB] text-[#999999]',
]"
>
{{
card.status === 'Y'
? tm('Secure_Enabled')
: tm('Secure_Disabled')
}}
</span>
</div>
<!-- Title -->
<h4
:class="[
'text-[#1F1F1F] font-bold',
locale === 'ko'
? 'text-[18px] md:text-[24px] leading-[26px] md:leading-[34px] tracking-[-0.54px] md:tracking-[-0.72px]'
: 'text-[18px] md:text-[24px] leading-[26px] md:leading-[34px] tracking-[-0.54px] md:tracking-[-0.72px]',
]"
>
{{ card.title }}
</h4>
<!-- Description -->
<p
:class="[
'flex-1 text-[#999999] font-[400]',
locale === 'ko'
? 'text-[14px] md:text-base leading-[22px] md:leading-[26px] tracking-[-0.42px] md:tracking-[-0.48px]'
: 'text-[14px] md:text-base leading-[24px] md:leading-[26px] tracking-[-0.42px] md:tracking-[-0.48px]',
]"
>
{{ card.description }}
</p>
</div>
<!-- Benefit Section -->
<div
:class="[
'rounded-2xl flex flex-col',
locale === 'ko'
? 'self-stretch p-[10px] md:p-4 gap-4'
: 'p-[10px] md:p-4 gap-3 md:gap-4',
card.benefitTitle ? 'bg-[#F0F4FF]' : '',
]"
>
<!-- Benefit Info -->
<div
v-if="card.benefitTitle"
:class="[
'flex items-center',
locale === 'ko' ? 'gap-[12px]' : 'gap-2 md:gap-[12px]',
]"
>
<div
v-if="card.benefitIcon"
:class="[
'bg-[#3C75FF] rounded-[8px] flex items-center justify-center',
locale === 'ko'
? 'w-[48px] h-[48px]'
: 'w-[40px] h-[40px] md:w-[48px] md:h-[48px]',
]"
>
<img
:src="
formatPathHost(card.benefitIcon, { imageType: 'common' })
"
:alt="card.benefitTitle"
:class="[
'object-contain rounded-2xl',
locale === 'ko'
? 'w-[48px] h-[48px]'
: 'w-[40px] h-[40px] md:w-[48px] md:h-[48px]',
]"
loading="lazy"
draggable="false"
/>
</div>
<div class="flex-1 flex flex-col text-left">
<div
:class="[
'text-[#3C75FF] font-bold',
locale === 'ko'
? 'text-[14px] md:text-[18px] leading-[22px] md:leading-[26px] tracking-[-0.42px] md:tracking-[-0.54px]'
: 'text-[14px] md:text-[18px] leading-[20px] md:leading-[26px] tracking-[-0.42px] md:tracking-[-0.54px]',
]"
>
{{ card.benefitTitle }}
</div>
<div
v-if="card.benefitDesc"
:class="[
'text-[#3C75FF] font-[400] opacity-90',
locale === 'ko'
? 'text-[12px] md:text-[13px] leading-[18px] md:leading-[22px] tracking-[-0.325px]'
: 'text-[12px] md:text-[13px] leading-[18px] md:leading-[22px] tracking-[-0.24px] md:tracking-[-0.325px]',
]"
>
{{ card.benefitDesc }}
</div>
</div>
</div>
<!-- Button -->
<AtomsButton
v-if="card.status === 'N'"
type="external"
button-size="size-small md:size-large"
background-color="#383838"
text-color="#FFFFFF"
@click="
isLogin
? handleSecureSetting(card.url)
: checkLoginValidation()
"
>
<span>{{ tm('Secure_Action_setup') }}</span>
</AtomsButton>
<AtomsButton
v-else
type="action"
button-size="size-small md:size-large"
background-color="#EBEBEB"
text-color="#999999"
disabled
>
<span>{{ tm('Secure_Action_complete') }}</span>
<svg
width="16"
height="18"
viewBox="0 0 16 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M1.4298 2.80644L6.84645 0.240655C7.52385 -0.0802185 8.30948 -0.0802184 8.98688 0.240655L14.4035 2.80645C15.2767 3.22003 15.8333 4.09952 15.8333 5.06564V7.65038C15.8333 13.399 10.6191 16.1288 8.65401 16.9535C8.18024 17.1523 7.6531 17.1523 7.17932 16.9535C5.21423 16.1288 -0.000131724 13.399 2.49573e-09 7.65038L1.11287e-05 5.06566C6.95637e-06 4.09953 0.556675 3.22002 1.4298 2.80644ZM11.4226 7.4063C11.748 7.08086 11.748 6.55323 11.4226 6.22779C11.0972 5.90235 10.5695 5.90235 10.2441 6.22779L7.5 8.97187L6.00592 7.47779C5.68049 7.15235 5.15285 7.15235 4.82741 7.47779C4.50197 7.80323 4.50197 8.33086 4.82741 8.6563L6.91074 10.7396C7.23618 11.0651 7.76382 11.0651 8.08926 10.7396L11.4226 7.4063Z"
fill="#999999"
/>
</svg>
</AtomsButton>
</div>
</div>
</div>
<!-- Caution Section -->
<div
:class="[
'bg-[#FAFAFA] rounded-2xl flex flex-col text-left',
locale === 'ko'
? 'self-stretch p-8 mb-6 gap-3'
: 'w-full md:flex-1 p-5 md:p-8 gap-2 md:gap-3',
]"
>
<h5
:class="[
'text-[#333333] font-bold',
locale === 'ko'
? 'text-[20px] leading-[30px] tracking-[-0.6px]'
: 'text-[16px] md:text-[20px] leading-[24px] md:leading-[30px] tracking-[-0.48px] md:tracking-[-0.6px]',
]"
>
{{ tm('Secure_Notice') }}
</h5>
<ul class="relative flex flex-col items-start justify-start w-full">
<li
v-for="caution in cautionText"
:key="caution"
v-dompurify-html="caution"
:class="[
'relative pl-[22px] before:content-[\'\'] before:absolute before:top-[10px] before:left-[9px] before:w-[3px] before:h-[3px] before:rounded-full before:bg-[#999999] text-[#999999] font-[400]',
locale === 'ko'
? 'text-[14px] leading-[24px] tracking-[-0.42px]'
: 'text-[14px] leading-[24px] tracking-[-0.42px]',
]"
></li>
</ul>
</div>
</div>
</div>
</section>
</div>
</template>
<style scoped>
:deep(.btn-base.disabled) {
@apply border-solid border border-[#d3d3d3];
}
:deep(.btn-base.disabled .btn-content) {
@apply opacity-100;
}
</style>