feat. FX_PREREGIST_01 템플릿 제작

This commit is contained in:
clkim
2025-11-06 21:01:56 +09:00
parent a2112f7b2d
commit e943f3a9a2
9 changed files with 1490 additions and 530 deletions

View File

@@ -2,6 +2,8 @@
import { SplideSlide } from '@splidejs/vue-splide'
import { getComponentGroup, getComponentGroupAry } from '#layers/utils/dataUtil'
import type { PageDataTemplateComponents } from '#layers/types/api/pageData'
import type { OperateGroupItem } from '#layers/types/api/resourcesData'
import type { Platform } from '#layers/types/components/button'
interface Props {
components: PageDataTemplateComponents
@@ -10,598 +12,357 @@ interface Props {
const props = defineProps<Props>()
const { tm } = useI18n()
const test = {
subTitle: {
groups: [
{
display: {
text: '사전 등록 기간 + 2024.06.04 ~ 정식 오픈 전까지',
color_code: '',
color_name: 'primary',
},
resource_type: 'TXT',
},
],
},
mainTitle: {
groups: [
{
display: {
text: '로드나인 사전 등록',
color_code: '',
color_name: 'primary',
},
resource_type: 'TXT',
},
],
},
background: {
groups: [
{
res_path: {
path_mo: '/local/template/l9/16/1/1/FX_PREREGIST_01/common/bg_m.jpg',
path_pc: '/local/template/l9/16/1/1/FX_PREREGIST_01/common/bg.jpg',
},
resource_type: 'IMG_COMM',
},
],
},
txtSnsLink: {
groups: [
{
display: {
text: 'https://pf.kakao.com/_xmyxjpG',
},
resource_type: 'TXT',
},
{
display: {
text: 'https://www.youtube.com/@LORDNINE_KR',
},
resource_type: 'TXT',
},
],
},
description: {
groups: [
{
display: {
text: '로드나인은 PC, GooglePlay, AppStore에서 즐기실 수 있습니다',
color_code: '',
color_name: 'text-primary',
},
resource_type: 'TXT',
},
],
},
imgAccReward: {
groups: [
{
display: {
text: '골드 100,000',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward01_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward01.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '중급 물약 x100',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward02_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward02.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '방어구 강화석 x3',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward03_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward03.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '무기 강화석 x3',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward04_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward04.png',
},
resource_type: 'IMG_LANG',
},
// {
// display: {
// text: '순수한 무기 강화석 x1',
// },
// res_path: {
// path_mo:
// '/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward05_m.png',
// path_pc:
// '/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward05.png',
// },
// resource_type: 'IMG_LANG',
// },
],
},
imgSnsButton: {
groups: [
{
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/sns_button01_m.jpg',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/sns_button01.jpg',
},
tracking: {
click_item: 'imgSnsButton_7. 사전등록-SNS버튼이미지(1)',
action_type: 'click',
click_sarea: 'promotionPreregist_tmpl_01__imgSnsButton',
},
resource_type: 'IMG_LANG',
},
{
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/sns_button02_m.jpg',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/sns_button02.jpg',
},
tracking: {
click_item: 'imgSnsButton_7. 사전등록-SNS버튼이미지(2)',
action_type: 'click',
click_sarea: 'promotionPreregist_tmpl_01__imgSnsButton',
},
resource_type: 'IMG_LANG',
},
],
},
imgSnsReward: {
groups: [
{
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_preregist_reward_sns_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_preregist_reward_sns.png',
},
resource_type: 'IMG_LANG',
},
],
},
subTitleAccReward: {
groups: [
{
display: {
text: '이벤트 기간 + 2024.06.04 ~ 정식 오픈 전까지',
color_code: '#c5902f',
color_name: '',
},
resource_type: 'TXT',
},
],
},
imgPreregistReward: {
groups: [
{
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_preregist_reward_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_preregist_reward.png',
},
resource_type: 'IMG_LANG',
},
],
},
mainTitleAccReward: {
groups: [
{
display: {
text: '카카오톡 공식 채널 구독 이벤트',
color_code: '#c5902f',
color_name: '',
},
resource_type: 'TXT',
},
],
},
backgroundAccReward: {
groups: [
{
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/common/bg_acc_reward_m.jpg',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/common/bg_acc_reward.jpg',
},
resource_type: 'IMG_COMM',
},
],
},
descriptionAccReward: {
groups: [
{
display: {
text: '※ 달성 여부는 실시간으로 반영되지 않을 수 있습니다.',
color_code: '#737474',
color_name: '',
},
resource_type: 'TXT',
},
],
},
isAccRewardCompleted: {},
preregistButtonColor: {
groups: [
{
display: {
color_code: '#cc0000',
color_name: '',
},
resource_type: 'COLOR',
},
{
display: {
color_code: '#ccff00',
color_name: '',
},
resource_type: 'COLOR',
},
],
},
rewardTitleAccReward: {
groups: [
{
display: {
text: '누적 목표를 달성할수록 더 많은 보상을 드립니다!',
color_code: '#33312e',
color_name: '',
},
resource_type: 'TXT',
},
],
},
imgAccRewardIncomplete: {
groups: [
{
display: {
text: '골드 100,000',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete01_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete01.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '중급 물약 x100',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete02_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete02.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '방어구 강화석 x3',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete03_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete03.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '무기 강화석 x3',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete04_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete04.png',
},
resource_type: 'IMG_LANG',
},
{
display: {
text: '순수한 무기 강화석 x1',
},
res_path: {
path_mo:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete05_m.png',
path_pc:
'/local/template/l9/16/1/1/FX_PREREGIST_01/ko/img_acc_reward_incomplete05.png',
},
resource_type: 'IMG_LANG',
},
],
},
} as any
const { locale } = useI18n()
const { getOperateResourcesData } = useResourcesData()
const { gameData } = storeToRefs(useGameDataStore())
const { pageData } = storeToRefs(usePageDataStore())
// Constants
const PREREGIST_PLATFORMS = [
{ platform: 'stove', label: '사전 등록 하기' },
{ platform: 'google_play', label: '사전 등록 하기' },
{ platform: 'app_store', label: '사전 등록 하기' },
] as const
const COLOR_INDEX = { BACKGROUND: 0, TEXT: 1 } as const
const OS_TYPE_MAP: Record<string, Platform[]> = {
'1': ['google_play'],
'2': ['app_store'],
'3': ['google_play', 'app_store'],
}
const COLOR_INDEX = {
BACKGROUND: 0,
TEXT: 1,
} as const
const preregistModalRef = ref<{ handlePreregist: () => void } | null>(null)
// Preregist Section
const backgroundData = computed(() =>
const preregistCode = computed(
() => getComponentGroup(props.components, 'preregistCode')?.display?.text
)
const prdBackgroundData = computed(() =>
getComponentGroup(props.components, 'background')
)
const mainTitleData = computed(() =>
const preMainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitle')
)
const subTitleData = computed(() =>
const preSubTitleData = computed(() =>
getComponentGroup(props.components, 'subTitle')
)
const imgPreregistRewardData = computed(() =>
const preImgPreregistdData = computed(() =>
getComponentGroup(props.components, 'imgPreregistReward')
)
const imgSnsRewardData = computed(() =>
const preImgSnsData = computed(() =>
getComponentGroup(props.components, 'imgSnsReward')
)
const preregistButtonColorData = computed(() =>
getComponentGroupAry(props.components, 'preregistButtonColor')
)
const imgSnsButtonData = computed(() =>
getComponentGroupAry(props.components, 'imgSnsButton')
)
const txtSnsLinkData = computed(() =>
getComponentGroupAry(props.components, 'txtSnsLink')
)
const descriptionData = computed(() =>
const preDescriptionData = computed(() =>
getComponentGroup(props.components, 'description')
)
const snsButtons = computed(() => {
const buttons = imgSnsButtonData.value
const links = txtSnsLinkData.value
// SNS Buttons
const snsButtonsData = computed(() => {
const buttons = getComponentGroupAry(props.components, 'imgSnsButton')
const links = getComponentGroupAry(props.components, 'txtSnsLink')
if (!buttons?.length) return []
return buttons.map((button, index) => ({
image: button,
link: links?.[index]?.display?.text ?? '',
id: button.id ?? `sns-${index}`,
}))
})
const buttonColors = computed(() => {
const colorData = preregistButtonColorData.value
if (!colorData?.length) {
return { background: undefined, text: undefined }
}
// Button Colors
const buttonColors = computed(() => {
const colorData = getComponentGroupAry(
props.components,
'preregistButtonColor'
)
if (!colorData?.length)
return { backgroundColor: undefined, textColor: undefined }
return {
background: getColorCode({
backgroundColor: getColorCode({
colorName: colorData[COLOR_INDEX.BACKGROUND]?.display?.color_name,
colorCode: colorData[COLOR_INDEX.BACKGROUND]?.display?.color_code,
}),
text: getColorCode({
textColor: getColorCode({
colorName: colorData[COLOR_INDEX.TEXT]?.display?.color_name,
colorCode: colorData[COLOR_INDEX.TEXT]?.display?.color_code,
}),
}
})
// Platform Buttons
const platformButtons = computed<Platform[]>(() => {
const osType = String(gameData.value?.os_type ?? '')
return OS_TYPE_MAP[osType] ?? []
})
// Reward Section
const backgroundAccRewardData = computed(() =>
const accBackgroundData = computed(() =>
getComponentGroup(props.components, 'backgroundAccReward')
)
const mainTitleAccRewardData = computed(() =>
const accMainTitleData = computed(() =>
getComponentGroup(props.components, 'mainTitleAccReward')
)
const subTitleAccRewardData = computed(() =>
const accSubTitleData = computed(() =>
getComponentGroup(props.components, 'subTitleAccReward')
)
const rewardTitleAccRewardData = computed(() =>
const accRewardTitleData = computed(() =>
getComponentGroup(props.components, 'rewardTitleAccReward')
)
const imgAccRewardData = computed(() =>
getComponentGroupAry(test, 'imgAccReward')
)
const imgAccRewardLength = computed(() => imgAccRewardData.value?.length ?? 0)
const descriptionAccRewardData = computed(() =>
const accDescriptionData = computed(() =>
getComponentGroup(props.components, 'descriptionAccReward')
)
const accPaginationData = computed(() =>
getComponentGroupAry(props.components, 'pagination')
)
// Async Data - 리워드 완료 데이터
const { data: rewardCompletedData } = await useAsyncData(
`fx-preregist-resources-${pageData.value?.page_seq}-${pageData.value?.page_ver}-${props.pageVerTmplSeq}`,
async () => {
const { page_seq, page_ver } = pageData.value ?? {}
if (!page_seq || !page_ver) return []
try {
const operateGroupList = await getOperateResourcesData({
pageSeq: page_seq,
pageVer: page_ver,
pageVerTmplSeq: props.pageVerTmplSeq,
langCode: locale.value,
})
return getComponentContainer(operateGroupList, 'isAccRewardCompleted', {
isGroup: true,
}) as OperateGroupItem[]
} catch (error) {
if (import.meta.dev) {
// eslint-disable-next-line no-console
console.error('[FxPreregist01] Failed to fetch reward data:', error)
}
return []
}
},
{
default: () => [],
server: false,
}
)
// Reward Images
const rewardImages = computed(() => {
const defaultList = getComponentGroupAry(props.components, 'imgAccReward')
const incompleteList = getComponentGroupAry(
props.components,
'imgAccRewardIncomplete'
)
if (!defaultList?.length) return []
return defaultList.map((defaultItem, index) => ({
id: defaultItem.id ?? `reward-${index}`,
default: defaultItem,
incomplete: incompleteList?.[index] ?? null,
flagType: rewardCompletedData.value?.[index]?.flag_type ?? 0,
}))
})
// Splide Options
const splideOptions = computed(() => {
const length = rewardImages.value.length
return {
type: 'slide',
type: 'slide' as const,
gap: 16,
arrows: false,
pagination: false,
destroy: true,
breakpoints: {
[BREAKPOINTS.md - 1]: {
destroy: imgAccRewardLength.value <= 3,
perPage: 'auto',
focus: 'center',
drag: imgAccRewardLength.value > 3,
padding: {
left: 40,
right: 40,
},
937: {
destroy: length < 5,
gap: 12,
padding: { left: 40, right: 40 },
},
[BREAKPOINTS.sm - 1]: {
padding: { left: 20, right: 20 },
},
723: {
destroy: length < 4,
},
561: {
destroy: false,
gap: 12,
drag: true,
padding: {
left: 20,
right: 20,
},
},
},
}
})
// Handler
const handlePreregistClick = () => {
preregistModalRef.value?.handlePreregist()
}
</script>
<template>
<section class="relative py-[80px] md:py-[120px]">
<WidgetsBackground v-if="backgroundData" :resources-data="backgroundData" />
<div class="content-standard">
<WidgetsMainTitle
v-if="mainTitleData"
:resources-data="mainTitleData"
class="title-xlg"
<div class="section-container">
<!-- Preregist Section -->
<section class="relative py-[80px] md:py-[120px]">
<WidgetsBackground
v-if="prdBackgroundData"
:resources-data="prdBackgroundData"
/>
<WidgetsSubTitle
v-if="subTitleData"
:resources-data="subTitleData"
class="title-sm mt-2"
/>
<div class="flex flex-col gap-4 mt-8 md:flex-row">
<div v-if="imgPreregistRewardData" class="w-full max-w-[446px]">
<AtomsImg
:src="getImagePaths(imgPreregistRewardData)"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
</div>
<div v-if="imgSnsRewardData" class="relative w-full max-w-[446px]">
<AtomsImg
:src="getImagePaths(imgSnsRewardData)"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
<ul
v-if="snsButtons.length"
class="absolute bottom-[20px] left-0 w-full flex items-center justify-center gap-2 md:bottom-[24px] md:gap-3"
>
<li
v-for="(snsButton, index) in snsButtons"
:key="`sns-${snsButton.link}-${index}`"
>
<a
:href="snsButton.link"
target="_blank"
rel="noopener noreferrer"
>
<AtomsImg :src="getImagePaths(snsButton.image)" />
<span class="sr-only">
{{ snsButton.link }}
</span>
</a>
</li>
</ul>
</div>
</div>
<div
v-if="PREREGIST_PLATFORMS.length"
class="flex gap-3 justify-center flex-wrap mt-8 md:gap-2.5"
>
<AtomsButtonLauncher
v-for="item in PREREGIST_PLATFORMS"
:key="`preregist-${item.platform}`"
type="duplication"
:platform="item.platform"
:background-color="buttonColors.background"
:text-color="buttonColors.text"
>
{{ item.label }}
</AtomsButtonLauncher>
</div>
<WidgetsDescription
v-if="descriptionData"
:resources-data="descriptionData"
class="mt-8"
/>
</div>
</section>
<section class="relative py-[80px] md:py-[120px]">
<WidgetsBackground
v-if="backgroundAccRewardData"
:resources-data="backgroundAccRewardData"
/>
<div class="content-standard">
<WidgetsMainTitle
v-if="mainTitleAccRewardData"
:resources-data="mainTitleAccRewardData"
class="title-xlg"
/>
<WidgetsSubTitle
v-if="subTitleAccRewardData"
:resources-data="subTitleAccRewardData"
class="title-sm mt-2"
/>
<WidgetsSubTitle
v-if="rewardTitleAccRewardData"
tag="h4"
:resources-data="rewardTitleAccRewardData"
class="mt-[48px] text-[18px] font-[700] leading-[26px] tracking-[-0.54px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:mt-[72px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
/>
<div v-if="imgAccRewardLength" class="mt-6 md:mt-8">
<ul class="hidden md:flex justify-center md:mb-[20px]">
<li
v-for="index in imgAccRewardLength"
:key="index"
class="flex items-center"
>
<span class="progress-bullet"></span>
<div v-if="index !== imgAccRewardLength" class="progress-bar">
<span class="progress-fill"></span>
</div>
</li>
</ul>
<BlocksSlideDefault
v-bind="splideOptions"
class="w-[100vw] mx-[-20px] sm:mx-[-40px] md:w-auto md:mx-auto"
>
<SplideSlide
v-for="(item, index) in imgAccRewardData"
:key="`reward-${item.id ?? index}`"
class="w-[162px] h-[228px] md:w-[176px] md:h-[249px]"
>
<div class="content-standard">
<WidgetsMainTitle
v-if="preMainTitleData"
:resources-data="preMainTitleData"
class="title-xlg"
/>
<WidgetsSubTitle
v-if="preSubTitleData"
:resources-data="preSubTitleData"
class="title-sm mt-2"
/>
<div class="flex flex-col gap-4 mt-8 md:flex-row">
<div v-if="preImgPreregistdData" class="w-full max-w-[446px]">
<AtomsImg
:src="getImagePaths(item)"
:alt="item?.display?.text ?? `Reward ${index + 1}`"
:src="getImagePaths(preImgPreregistdData)"
:alt="preImgPreregistdData?.display?.text"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
</SplideSlide>
</BlocksSlideDefault>
</div>
<div v-if="preImgSnsData" class="relative w-full max-w-[446px]">
<AtomsImg
:src="getImagePaths(preImgSnsData)"
:alt="preImgSnsData?.display?.text"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
<ul
v-if="snsButtonsData.length"
class="absolute bottom-[20px] left-0 w-full flex items-center justify-center gap-2 md:bottom-[24px] md:gap-3"
>
<li
v-for="btn in snsButtonsData"
:key="btn.id"
class="w-[48px] h-[40px] md:w-[72px] md:h-[56px]"
>
<a :href="btn.link" target="_blank" rel="noopener noreferrer">
<AtomsImg
:src="getImagePaths(btn.image)"
:alt="btn.image?.display?.text"
/>
</a>
</li>
</ul>
</div>
</div>
<div class="flex gap-3 justify-center flex-wrap mt-8 md:gap-2.5">
<AtomsButtonLauncher
type="duplication"
platform="stove"
:background-color="buttonColors.backgroundColor"
:text-color="buttonColors.textColor"
@click="handlePreregistClick"
>
사전 등록 하기
</AtomsButtonLauncher>
<AtomsButtonLauncher
v-for="platform in platformButtons"
:key="`preregist-${platform}`"
type="duplication"
:platform="platform"
:background-color="buttonColors.backgroundColor"
:text-color="buttonColors.textColor"
>
사전 등록 하기
</AtomsButtonLauncher>
</div>
<WidgetsDescription
v-if="preDescriptionData"
:resources-data="preDescriptionData"
class="mt-8"
/>
</div>
<WidgetsDescription
v-if="descriptionAccRewardData"
:resources-data="descriptionAccRewardData"
class="mt-6 md:mt-8"
</section>
<!-- Reward Section -->
<section class="relative py-[80px] md:py-[120px]">
<WidgetsBackground
v-if="accBackgroundData"
:resources-data="accBackgroundData"
/>
</div>
</section>
<div class="content-standard">
<WidgetsMainTitle
v-if="accMainTitleData"
:resources-data="accMainTitleData"
class="title-xlg"
/>
<WidgetsSubTitle
v-if="accSubTitleData"
:resources-data="accSubTitleData"
class="title-sm mt-2"
/>
<WidgetsSubTitle
v-if="accRewardTitleData"
tag="h4"
:resources-data="accRewardTitleData"
class="mt-[48px] text-[18px] font-[700] leading-[26px] tracking-[-0.54px] drop-shadow-[0_2px_2px_rgba(0,0,0,0.6)] md:mt-[72px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
/>
<div
v-if="rewardImages.length"
class="overflow-hidden w-[calc(100%+40px)] min-h-[228px] mt-6 mx-[-20px] sm:w-[calc(100%+80px)] sm:mx-[-40px] md:w-full md:min-h-[281px] md:mt-8 md:mx-auto"
>
<ClientOnly>
<ul
class="hidden md:flex justify-center md:mb-[20px]"
:style="getPaginationClass(accPaginationData)"
>
<li
v-for="(item, index) in rewardImages"
:key="item.id"
:class="[
'flex items-center',
{ 'is-completed': item.flagType === 1 },
]"
>
<span class="progress-bullet"></span>
<div
v-if="index < rewardImages.length - 1"
class="progress-bar"
></div>
</li>
</ul>
<BlocksSlideDefault v-bind="splideOptions">
<SplideSlide
v-for="item in rewardImages"
:key="item.id"
class="w-[162px] h-[228px] md:w-[176px] md:h-[249px]"
>
<AtomsImg
:src="
getImagePaths(
item.flagType === 1 ? item.incomplete : item.default
)
"
:alt="item.default?.display?.text"
loading="lazy"
decoding="async"
class="w-full h-full object-contain"
/>
</SplideSlide>
</BlocksSlideDefault>
</ClientOnly>
</div>
<WidgetsDescription
v-if="accDescriptionData"
:resources-data="accDescriptionData"
class="mt-6 md:mt-8"
/>
</div>
</section>
<WidgetsModalPreregist
ref="preregistModalRef"
:preregist-code="preregistCode"
/>
</div>
</template>
<style scoped>
@@ -612,16 +373,14 @@ const splideOptions = computed(() => {
.progress-bullet {
@apply block w-3 h-3 rounded-full transition-all duration-300;
background-color: red;
/* background-color: var(--pagination-disabled); */
background-color: var(--pagination-disabled);
}
.progress-bar {
@apply relative w-[180px] h-0.5 overflow-hidden;
/* background-color: var(--pagination-disabled); */
background-color: red;
@apply w-[180px] h-0.5 overflow-hidden;
background-color: var(--pagination-disabled);
}
.progress-fill {
@apply absolute inset-y-0 left-0 w-[0];
.is-completed .progress-bullet,
.is-completed .progress-bar {
background-color: var(--pagination-active);
}
</style>