feat: 쿠폰등록
This commit is contained in:
58
layers/components/atoms/Input.vue
Normal file
58
layers/components/atoms/Input.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
modelValue: string
|
||||
placeholder?: string
|
||||
useClearButton?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
useClearButton: false,
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'input', 'keydown', 'clear'])
|
||||
|
||||
const localValue = computed({
|
||||
get: () => props.modelValue,
|
||||
set: value => emit('update:modelValue', value),
|
||||
})
|
||||
|
||||
const onClickClearButton = () => {
|
||||
localValue.value = ''
|
||||
emit('clear')
|
||||
}
|
||||
|
||||
const onInput = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement | null
|
||||
if (target) {
|
||||
localValue.value = target.value
|
||||
}
|
||||
emit('input')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="group relative w-full">
|
||||
<input
|
||||
v-model="localValue"
|
||||
:type="typeof $attrs.type === 'string' ? $attrs.type : 'text'"
|
||||
:placeholder="props.placeholder"
|
||||
v-bind="$attrs"
|
||||
class="relative w-full h-[48px] px-[12px] outline-none border border-solid border-[1px] border-[#D9D9D9] rounded-[8px] bg-white text-left text-[#333333] text-[14px] font-[400] leading-[20px] tracking-[-0.42px] placeholder:text-[#B2B2B2] md:h-[56px] md:px-[16px] md:text-[16px] md:leading-[26px] md:tracking-[-0.48px] hover:[&:not([readonly])]:border-[#999999] focus:border-[#999999]"
|
||||
@input="onInput"
|
||||
@keydown="emit('keydown', $event)"
|
||||
/>
|
||||
<AtomsButton
|
||||
v-if="props.useClearButton && localValue.length > 0"
|
||||
type="action"
|
||||
button-size="size-small"
|
||||
background-color="#00000000"
|
||||
text-color="transparent"
|
||||
class="!absolute top-[50%] right-[12px] translate-y-[-50%] flex items-center justify-center w-auto h-auto p-0 md:right-[16px]"
|
||||
@click="onClickClearButton"
|
||||
>
|
||||
<AtomsIconsCloseCircleFill :size="16" color="rgba(0,0,0,0.15)" />
|
||||
</AtomsButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
99
layers/components/atoms/Select.vue
Normal file
99
layers/components/atoms/Select.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
options: Array<any>
|
||||
labelName: string | number
|
||||
modelValue?: object
|
||||
placeholder?: string
|
||||
defaultLabel?: string
|
||||
placement?: string | 'top' | 'bottom'
|
||||
className?: string
|
||||
selectedColor?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: null,
|
||||
placeholder: null,
|
||||
defaultLabel: null,
|
||||
placement: 'bottom',
|
||||
className: '',
|
||||
selectedColor: '#333333',
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const isActive = ref(false)
|
||||
const isSelected = ref(props.defaultLabel !== null)
|
||||
const selectedOption = ref<string>(props.placeholder || props.defaultLabel)
|
||||
|
||||
const onToggleOpen = () => {
|
||||
isActive.value = !isActive.value
|
||||
}
|
||||
|
||||
const onSelectOption = (option: { [key: string | number]: any }): void => {
|
||||
isActive.value = false
|
||||
isSelected.value = true
|
||||
selectedOption.value = option[props.labelName]
|
||||
emits('update:modelValue', option)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative w-full max-w-[432px]">
|
||||
<button
|
||||
type="button"
|
||||
class="relative flex items-center justify-between gap-[12px] w-full py-[12px] px-[16px] bg-white border border-solid border-[1px] border-[#D9D9D9] rounded-[8px]"
|
||||
@click="onToggleOpen"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-left text-left text-[14px] font-[400] leading-[20px] tracking-[-0.42px]"
|
||||
:class="isSelected ? 'text-[#333333]' : 'text-[#999999]'"
|
||||
>
|
||||
{{ selectedOption }}
|
||||
</span>
|
||||
<i
|
||||
class="inline-flex items-center justify-center w-[14px] h-[14px] shrink-0"
|
||||
>
|
||||
<AtomsIconsSelectArrowDownFill
|
||||
:size="12"
|
||||
color="#333333"
|
||||
:class="isActive ? 'rotate-180' : ''"
|
||||
/>
|
||||
</i>
|
||||
</button>
|
||||
<div
|
||||
v-if="isActive"
|
||||
:data-placement="props.placement"
|
||||
class="absolute z-[10] top-full left-0 translate-y-[4px] w-full py-[8px] border border-solid border-[1px] border-[rgba(0,0,0,0.3)] rounded-[8px] bg-white shadow-[0_4px_10px_0_rgba(0,0,0,0.10)]"
|
||||
>
|
||||
<ul class="relative flex flex-col items-center justify-start w-full">
|
||||
<li
|
||||
v-for="(option, index) in props.options"
|
||||
:key="String(option[props.labelName])"
|
||||
class="relative flex items-center justify-left w-full"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="relative flex items-center justify-left w-full py-[8px] pl-[40px] pr-[16px] bg-white text-left text-left text-[14px] font-[400] leading-[24px] tracking-[-0.42px] hover:bg-[rgba(0,0,0,0.04)]"
|
||||
:class="
|
||||
selectedColor ? `text-[${selectedColor}]` : 'text-[#333333]'
|
||||
"
|
||||
@click="onSelectOption(option)"
|
||||
>
|
||||
<template v-if="option[props.labelName] === selectedOption">
|
||||
<i
|
||||
class="absolute top-1/2 left-[12px] translate-y-[-50%] flex items-center justify-center w-[20px] h-[20px]"
|
||||
>
|
||||
<AtomsIconsCheckLine :size="16" :color="selectedColor" />
|
||||
</i>
|
||||
</template>
|
||||
<span>
|
||||
{{ option[props.labelName] }}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
34
layers/components/atoms/icons/ArrowDoubleLeftLine.vue
Normal file
34
layers/components/atoms/icons/ArrowDoubleLeftLine.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: 'var(--foreground-gray-500)',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 11 11"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M5.80474 1.13807C6.06509 0.877722 6.06509 0.455612 5.80474 0.195262C5.54439 -0.0650874 5.12228 -0.0650874 4.86193 0.195262L0.195262 4.86193C-0.0650874 5.12228 -0.0650874 5.54439 0.195262 5.80474L4.86193 10.4714C5.12228 10.7318 5.54439 10.7318 5.80474 10.4714C6.06509 10.2111 6.06509 9.78894 5.80474 9.52859L1.60948 5.33333L5.80474 1.13807Z"
|
||||
:fill="color"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10.4714 1.13807C10.7318 0.877722 10.7318 0.455612 10.4714 0.195262C10.2111 -0.0650874 9.78895 -0.0650874 9.5286 0.195262L4.86193 4.86193C4.60158 5.12228 4.60158 5.54439 4.86193 5.80474L9.5286 10.4714C9.78895 10.7318 10.2111 10.7318 10.4714 10.4714C10.7318 10.2111 10.7318 9.78894 10.4714 9.52859L6.27614 5.33333L10.4714 1.13807Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
32
layers/components/atoms/icons/CalendarLine.vue
Normal file
32
layers/components/atoms/icons/CalendarLine.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#EBEBEB',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M5.83333 11.4583C5.83333 10.6529 5.18042 10 4.375 10C3.56958 10 2.91667 10.6529 2.91667 11.4583C2.91667 12.2637 3.56958 12.9167 4.375 12.9167C5.18041 12.9167 5.83333 12.2637 5.83333 11.4583Z"
|
||||
:fill="color"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.75 0C4.21024 0 4.58333 0.373096 4.58333 0.833333V1.66667H10.4167V0.833333C10.4167 0.373096 10.7898 0 11.25 0C11.7102 0 12.0833 0.373096 12.0833 0.833333V1.66667H12.9167C14.0673 1.66667 15 2.59941 15 3.75V13.75C15 14.9006 14.0673 15.8333 12.9167 15.8333H2.08333C0.93274 15.8333 0 14.9006 0 13.75V3.75C0 2.59941 0.93274 1.66667 2.08333 1.66667H2.91667V0.833333C2.91667 0.373096 3.28976 0 3.75 0ZM10.4167 3.33333C10.4167 3.79357 10.7898 4.16667 11.25 4.16667C11.7102 4.16667 12.0833 3.79357 12.0833 3.33333H12.9167C13.1468 3.33333 13.3333 3.51988 13.3333 3.75V5.83333H1.66667V3.75C1.66667 3.51988 1.85321 3.33333 2.08333 3.33333H2.91667C2.91667 3.79357 3.28976 4.16667 3.75 4.16667C4.21024 4.16667 4.58333 3.79357 4.58333 3.33333H10.4167ZM1.66667 7.5H13.3333V13.75C13.3333 13.9801 13.1468 14.1667 12.9167 14.1667H2.08333C1.85321 14.1667 1.66667 13.9801 1.66667 13.75V7.5Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
28
layers/components/atoms/icons/CheckLine.vue
Normal file
28
layers/components/atoms/icons/CheckLine.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#1F1F1F',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 15 10"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M13.9226 0.244078C14.248 0.569515 14.248 1.09715 13.9226 1.42259L5.58927 9.75592C5.42514 9.92006 5.19998 10.0083 4.96803 9.99939C4.73609 9.99048 4.51836 9.88523 4.3673 9.70899L0.200635 4.84788C-0.0988839 4.49844 -0.0584159 3.97236 0.291022 3.67284C0.64046 3.37332 1.16654 3.41379 1.46606 3.76323L5.04708 7.94109L12.7441 0.244078C13.0695 -0.0813592 13.5972 -0.0813592 13.9226 0.244078Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
28
layers/components/atoms/icons/CloseCircleFill.vue
Normal file
28
layers/components/atoms/icons/CloseCircleFill.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#1F1F1F',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 1.5C17.799 1.5 22.5 6.20101 22.5 12C22.5 17.799 17.799 22.5 12 22.5C6.20101 22.5 1.5 17.799 1.5 12C1.50001 6.20101 6.20102 1.5 12 1.5ZM15.7071 15.7071C16.0976 15.3166 16.0976 14.6834 15.7071 14.2929L13.4142 12L15.7071 9.70711C16.0976 9.31659 16.0976 8.68342 15.7071 8.2929C15.3166 7.90237 14.6834 7.90237 14.2929 8.2929L12 10.5858L9.70711 8.2929C9.31659 7.90237 8.68342 7.90237 8.2929 8.2929C7.90238 8.68342 7.90238 9.31658 8.2929 9.70711L10.5858 12L8.2929 14.2929C7.90237 14.6834 7.90237 15.3166 8.2929 15.7071C8.68342 16.0976 9.31659 16.0976 9.70711 15.7071L12 13.4142L14.2929 15.7071C14.6834 16.0976 15.3166 16.0976 15.7071 15.7071Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
28
layers/components/atoms/icons/CouponFill.vue
Normal file
28
layers/components/atoms/icons/CouponFill.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#EBEBEB',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10.4413 0.644656C9.58368 -0.214056 8.18151 -0.214884 7.32284 0.64217L6.09332 1.85726L6.092 1.85863C6.08921 1.86072 6.08403 1.86443 6.07608 1.86947C6.05498 1.88286 6.02495 1.89889 5.98871 1.91393C5.95257 1.92893 5.91833 1.93961 5.89062 1.94597C5.8723 1.95017 5.86194 1.95131 5.8595 1.95158H4.15691C2.93936 1.95158 1.94911 2.9448 1.94911 4.16099V5.86353C1.9487 5.86679 1.94743 5.87584 1.94397 5.89078C1.93766 5.91805 1.92684 5.9528 1.91128 5.99038C1.89572 6.02795 1.87871 6.06042 1.86357 6.08467C1.85431 6.0995 1.84819 6.10737 1.8464 6.10964L0.643348 7.31422C-0.214449 8.17311 -0.214449 9.57689 0.643348 10.4358L1.8464 11.6404C1.84819 11.6426 1.85431 11.6505 1.86357 11.6653C1.87871 11.6896 1.89572 11.722 1.91128 11.7596C1.92684 11.7972 1.93766 11.8319 1.94397 11.8592C1.94743 11.8742 1.9487 11.8832 1.94911 11.8865V13.589C1.94911 14.8052 2.93936 15.7984 4.15691 15.7984H5.85707C5.86038 15.7988 5.86923 15.8001 5.88373 15.8035C5.91079 15.8098 5.94533 15.8205 5.98275 15.8361C6.02015 15.8516 6.05249 15.8686 6.07662 15.8837C6.09134 15.8929 6.09916 15.899 6.10143 15.9008L7.30447 17.1053C8.16292 17.9649 9.56698 17.9649 10.4254 17.1053L11.6285 15.9008C11.6307 15.899 11.6385 15.8929 11.6533 15.8837C11.6774 15.8686 11.7097 15.8516 11.7471 15.8361C11.7846 15.8205 11.8191 15.8098 11.8462 15.8035C11.8607 15.8001 11.8695 15.7988 11.8728 15.7984H13.573C14.7905 15.7984 15.7808 14.8052 15.7808 13.589V11.8865C15.7812 11.8832 15.7825 11.8742 15.7859 11.8592C15.7922 11.8319 15.8031 11.7972 15.8186 11.7596C15.8342 11.722 15.8512 11.6896 15.8663 11.6653C15.8756 11.6505 15.8817 11.6426 15.8835 11.6404L17.0774 10.4449C17.9753 9.58844 17.9649 8.17522 17.1001 7.31988L15.8994 6.11759C15.8976 6.11533 15.8915 6.10746 15.8822 6.09262C15.8671 6.06837 15.8501 6.0359 15.8345 5.99833C15.8189 5.96074 15.8081 5.926 15.8018 5.89872C15.7983 5.88378 15.7971 5.87473 15.7967 5.87147V4.16099C15.7967 2.94479 14.8064 1.95158 13.5889 1.95158H11.8887C11.8854 1.95116 11.8766 1.94989 11.8621 1.94653C11.835 1.94025 11.8004 1.92947 11.763 1.91393C11.7256 1.8984 11.6933 1.88142 11.6692 1.86632C11.6544 1.85711 11.6466 1.85102 11.6444 1.84922L10.4413 0.644656ZM12.3807 5.36843C12.7468 5.73455 12.7468 6.32814 12.3807 6.69425L6.69319 12.3818C6.32707 12.7479 5.73348 12.7479 5.36736 12.3818C5.00124 12.0156 5.00124 11.422 5.36736 11.0559L11.0549 5.36843C11.421 5.00231 12.0146 5.00231 12.3807 5.36843ZM6.23574 7.25014C6.79665 7.25014 7.25136 6.79543 7.25136 6.23451C7.25136 5.6736 6.79665 5.21889 6.23574 5.21889C5.67482 5.21889 5.22011 5.6736 5.22011 6.23451C5.22011 6.79543 5.67482 7.25014 6.23574 7.25014ZM11.5141 12.5312C12.075 12.5312 12.5297 12.0765 12.5297 11.5156C12.5297 10.9547 12.075 10.5 11.5141 10.5C10.9531 10.5 10.4984 10.9547 10.4984 11.5156C10.4984 12.0765 10.9531 12.5312 11.5141 12.5312Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
32
layers/components/atoms/icons/DesktopLine.vue
Normal file
32
layers/components/atoms/icons/DesktopLine.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#EBEBEB',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 17 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.83333 7.8125C5.3731 7.8125 5 8.1856 5 8.64583C5 9.10607 5.3731 9.47917 5.83333 9.47917H10.8333C11.2936 9.47917 11.6667 9.10607 11.6667 8.64583C11.6667 8.1856 11.2936 7.8125 10.8333 7.8125H5.83333Z"
|
||||
:fill="color"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3.33333 0C1.49238 0 0 1.49238 0 3.33333V9.58333C0 11.4243 1.49238 12.9167 3.33333 12.9167H7.5V13.6458H5.20833C4.7481 13.6458 4.375 14.0189 4.375 14.4792C4.375 14.9394 4.7481 15.3125 5.20833 15.3125H11.4583C11.9186 15.3125 12.2917 14.9394 12.2917 14.4792C12.2917 14.0189 11.9186 13.6458 11.4583 13.6458H9.16667V12.9167H13.3333C15.1743 12.9167 16.6667 11.4243 16.6667 9.58333V3.33333C16.6667 1.49238 15.1743 0 13.3333 0H3.33333ZM1.66667 3.33333C1.66667 2.41286 2.41286 1.66667 3.33333 1.66667H13.3333C14.2538 1.66667 15 2.41286 15 3.33333V9.58333C15 10.5038 14.2538 11.25 13.3333 11.25H3.33333C2.41286 11.25 1.66667 10.5038 1.66667 9.58333V3.33333Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
28
layers/components/atoms/icons/SearchLine.vue
Normal file
28
layers/components/atoms/icons/SearchLine.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#EBEBEB',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M9.74512 10.6879C8.71868 11.509 7.41668 12 6 12C2.68629 12 0 9.31371 0 6C0 2.68629 2.68629 0 6 0C9.31371 0 12 2.68629 12 6C12 7.41668 11.509 8.71868 10.6879 9.74512L13.1381 12.1953C13.3984 12.4556 13.3984 12.8777 13.1381 13.1381C12.8777 13.3984 12.4556 13.3984 12.1953 13.1381L9.74512 10.6879ZM10.6667 6C10.6667 8.57733 8.57733 10.6667 6 10.6667C3.42267 10.6667 1.33333 8.57733 1.33333 6C1.33333 3.42267 3.42267 1.33333 6 1.33333C8.57733 1.33333 10.6667 3.42267 10.6667 6Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
26
layers/components/atoms/icons/SelectArrowDownFill.vue
Normal file
26
layers/components/atoms/icons/SelectArrowDownFill.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
size: 32,
|
||||
color: '#EBEBEB',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 10 6"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.06454 4.95333L0.204544 1.16667C-0.228789 0.74 0.0712106 0 0.684544 0L9.31787 0C9.9312 0 10.2312 0.74 9.79787 1.16667L5.93787 4.95333C5.41787 5.46 4.59121 5.46 4.07121 4.95333H4.06454Z"
|
||||
:fill="color"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
524
layers/components/blocks/DatePicker.vue
Normal file
524
layers/components/blocks/DatePicker.vue
Normal file
@@ -0,0 +1,524 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
getYear,
|
||||
getMonth,
|
||||
startOfDay,
|
||||
globalDateFormat,
|
||||
} from '@seed-next/date'
|
||||
import type { PublicMethods } from '@vuepic/vue-datepicker'
|
||||
|
||||
interface Props {
|
||||
date: Date | string | null
|
||||
minDate?: Date | null
|
||||
maxDate?: Date | null
|
||||
placeholder?: Date | string | null
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
minDate: null,
|
||||
maxDate: null,
|
||||
placeholder: null,
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:date'])
|
||||
|
||||
// Composabled
|
||||
const breakpoints = useResponsiveBreakpoints()
|
||||
|
||||
// Refs -----
|
||||
const { tm, locale } = useI18n()
|
||||
const datePickerRef = ref<any>(null)
|
||||
const resultDate = ref('')
|
||||
const formatDate = ref('')
|
||||
const currentYear = ref(new Date().getFullYear())
|
||||
const currentMonth = ref(new Date().getMonth())
|
||||
const currentDecade = ref<{ start: number; end: number } | null>(null)
|
||||
// 상태값
|
||||
const isMenuOpened = ref(false)
|
||||
const isShowYearPicker = ref(false)
|
||||
const isShowMonthPicker = ref(false)
|
||||
const isShowDecadePicker = ref(false)
|
||||
|
||||
// Computed
|
||||
const yearMonthText = computed(() => {
|
||||
return `${currentYear.value}년 ${currentMonth.value + 1}월`
|
||||
})
|
||||
const formatMinDate = computed(() => {
|
||||
if (props.minDate !== null) {
|
||||
return globalDateFormat(props.minDate, locale.value)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
const formatMaxDate = computed(() => {
|
||||
if (props.maxDate !== null) {
|
||||
return globalDateFormat(props.maxDate, locale.value)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
})
|
||||
const formatPlaceholder = computed(() => {
|
||||
if (props.placeholder !== null) {
|
||||
return props.placeholder instanceof Date
|
||||
? (globalDateFormat(props.placeholder, locale.value) as string)
|
||||
: props.placeholder
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
// Functions
|
||||
const setFormatDate = () => {
|
||||
const toDay = startOfDay(new Date())
|
||||
resultDate.value =
|
||||
props.date === null ? toDay.toString() : props.date.toString()
|
||||
formatDate.value = globalDateFormat(resultDate.value, locale.value) as string
|
||||
|
||||
const dateObj = props.date === null ? toDay : new Date(props.date)
|
||||
currentYear.value = dateObj.getFullYear()
|
||||
currentMonth.value = dateObj.getMonth()
|
||||
}
|
||||
|
||||
const updateDate = (_date: Date | null) => {
|
||||
if (_date != null) {
|
||||
formatDate.value = globalDateFormat(_date, locale.value) as string
|
||||
currentYear.value = _date.getFullYear()
|
||||
currentMonth.value = _date.getMonth()
|
||||
emits('update:date', formatDate.value)
|
||||
}
|
||||
}
|
||||
|
||||
const format = (_date: Date) => {
|
||||
return globalDateFormat(_date, locale.value)
|
||||
}
|
||||
|
||||
const handleMenuOpen = () => {
|
||||
isMenuOpened.value = true
|
||||
}
|
||||
|
||||
const handleMenuClose = () => {
|
||||
isMenuOpened.value = false
|
||||
|
||||
isShowMonthPicker.value = false
|
||||
isShowYearPicker.value = false
|
||||
}
|
||||
|
||||
const handleOpenMonthPicker = () => {
|
||||
isShowMonthPicker.value = true
|
||||
isShowYearPicker.value = false
|
||||
}
|
||||
|
||||
const handleOpenYearPicker = () => {
|
||||
isShowYearPicker.value = true
|
||||
isShowMonthPicker.value = false
|
||||
}
|
||||
|
||||
const handleMonthSelect = (month: number) => {
|
||||
currentMonth.value = month
|
||||
isShowMonthPicker.value = false
|
||||
|
||||
// 선택한 년도/월로 날짜 업데이트
|
||||
const newDate = new Date(currentYear.value, currentMonth.value, 1)
|
||||
if (props.minDate && newDate < props.minDate) {
|
||||
newDate.setDate(props.minDate.getDate())
|
||||
}
|
||||
if (props.maxDate && newDate > props.maxDate) {
|
||||
newDate.setDate(props.maxDate.getDate())
|
||||
}
|
||||
|
||||
// DatePicker의 내부 상태 업데이트
|
||||
if (datePickerRef.value) {
|
||||
datePickerRef.value.selectDate(newDate)
|
||||
}
|
||||
|
||||
updateDate(newDate)
|
||||
}
|
||||
|
||||
const handleYearSelect = (year: number) => {
|
||||
currentYear.value = year
|
||||
isShowYearPicker.value = false
|
||||
|
||||
// 선택한 년도/월로 날짜 업데이트
|
||||
const newDate = new Date(currentYear.value, currentMonth.value, 1)
|
||||
if (props.minDate && newDate < props.minDate) {
|
||||
newDate.setDate(props.minDate.getDate())
|
||||
}
|
||||
if (props.maxDate && newDate > props.maxDate) {
|
||||
newDate.setDate(props.maxDate.getDate())
|
||||
}
|
||||
|
||||
// DatePicker의 내부 상태 업데이트
|
||||
if (datePickerRef.value) {
|
||||
datePickerRef.value.selectDate(newDate)
|
||||
}
|
||||
|
||||
updateDate(newDate)
|
||||
}
|
||||
|
||||
const generateYearList = () => {
|
||||
const years = []
|
||||
const minYear = props.minDate ? props.minDate.getFullYear() : 1900
|
||||
const maxYear = props.maxDate ? props.maxDate.getFullYear() : 2100
|
||||
|
||||
for (let year = minYear; year <= maxYear; year++) {
|
||||
years.push(year)
|
||||
}
|
||||
return years
|
||||
}
|
||||
|
||||
const generateMonthList = () => {
|
||||
const months = []
|
||||
const minMonth =
|
||||
props.minDate && props.minDate.getFullYear() === currentYear.value
|
||||
? props.minDate.getMonth()
|
||||
: 0
|
||||
const maxMonth =
|
||||
props.maxDate && props.maxDate.getFullYear() === currentYear.value
|
||||
? props.maxDate.getMonth()
|
||||
: 11
|
||||
|
||||
for (let month = minMonth; month <= maxMonth; month++) {
|
||||
months.push(month)
|
||||
}
|
||||
return months
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.date,
|
||||
() => {
|
||||
setFormatDate()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
setFormatDate()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VueDatePicker
|
||||
ref="datePickerRef"
|
||||
v-model="resultDate"
|
||||
:locale="locale"
|
||||
week-start="0"
|
||||
:year-first="true"
|
||||
:auto-apply="true"
|
||||
:hide-offset-dates="true"
|
||||
:enable-time-picker="false"
|
||||
:preview-format="format"
|
||||
:min-date="formatMinDate"
|
||||
:max-date="formatMaxDate"
|
||||
:prevent-min-max-navigation="true"
|
||||
:month-change-on-scroll="false"
|
||||
position="left"
|
||||
:auto-position="true"
|
||||
:offset="8"
|
||||
:config="{
|
||||
shadowDom: true,
|
||||
}"
|
||||
class="date-picker-wrap"
|
||||
@update:model-value="updateDate"
|
||||
@date-update="updateDate"
|
||||
@open="handleMenuOpen"
|
||||
@closed="handleMenuClose"
|
||||
>
|
||||
<template #trigger>
|
||||
<div
|
||||
class="relative flex items-center justify-between w-full h-[40px] px-[16px] border border-solid border-[1px] border-[#D9D9D9] rounded-[8px] bg-white cursor-pointer"
|
||||
>
|
||||
<AtomsInput
|
||||
:model-value="formatDate"
|
||||
:placeholder="formatPlaceholder"
|
||||
:value="formatDate"
|
||||
name="date-picker-text"
|
||||
label
|
||||
readonly
|
||||
class="inline-flex items-center justify-start !w-full !h-auto !p-0 m-0 bg-transparent border-none outline-none cursor-pointer text-left !text-[14px] text-[#333333] font-[400] !leading-[20px] !tracking-[-0.42px]"
|
||||
/>
|
||||
<AtomsIconsCalendarLine
|
||||
:size="breakpoints.isMobile ? 16 : 20"
|
||||
color="#333333"
|
||||
class="absolute top-[50%] right-[12px] translate-y-[-50%] md:right-[16px]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #calendar-header="{ index, day }">
|
||||
<div :class="index === 0 ? 'sunday' : index === 6 ? 'saturday' : ''">
|
||||
{{ day }}
|
||||
</div>
|
||||
</template>
|
||||
<template #day="{ day, date }">
|
||||
<span
|
||||
:class="[
|
||||
date.getDay() === 0 ? 'text-[#FC4420]' : 'text-[#333333]',
|
||||
date > new Date(formatMaxDate) ? 'opacity-35' : 'opacity-100',
|
||||
]"
|
||||
>
|
||||
{{ day }}
|
||||
</span>
|
||||
</template>
|
||||
<template
|
||||
#month-year="{
|
||||
month,
|
||||
year,
|
||||
months,
|
||||
years,
|
||||
|
||||
updateMonthYear,
|
||||
handleMonthYearChange,
|
||||
isDisabled,
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center justify-between w-full py-[16px]">
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="isDisabled(false)"
|
||||
@click="
|
||||
updateMonthYear(
|
||||
month === 0 ? 11 : month - 1,
|
||||
month === 0 ? year - 1 : year,
|
||||
false
|
||||
)
|
||||
"
|
||||
>
|
||||
<AtomsIconsArrowRightLine
|
||||
:size="16"
|
||||
:color="isDisabled(false) ? '#CCCCCC' : '#333333'"
|
||||
class="relative rotate-180"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center gap-[8px] w-auto h-[32px]"
|
||||
@click="handleOpenMonthPicker"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-center text-[#1F1F1F] text-[18px] font-[500] leading-[26px] tracking-[-0.54px]"
|
||||
>
|
||||
{{ years.find(y => y.value === year)?.text }}.
|
||||
{{ months.find(m => m.value === month)?.text }}
|
||||
</span>
|
||||
<AtomsIconsSelectArrowDownFill :size="10" color="#333333" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="isDisabled(true)"
|
||||
@click="
|
||||
updateMonthYear(
|
||||
month === 11 ? 0 : month + 1,
|
||||
month === 11 ? year + 1 : year,
|
||||
false
|
||||
)
|
||||
"
|
||||
>
|
||||
<AtomsIconsArrowRightLine
|
||||
:size="16"
|
||||
:color="isDisabled(true) ? '#CCCCCC' : '#333333'"
|
||||
class="relative"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="isShowMonthPicker && isMenuOpened"
|
||||
class="absolute z-[2] top-0 left-0 flex flex-col items-center justify-start w-full h-full overflow-y-auto bg-white"
|
||||
>
|
||||
<div class="flex items-center justify-between w-full py-[16px]">
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="isDisabled(false)"
|
||||
@click="
|
||||
updateMonthYear(
|
||||
month,
|
||||
year === getYear(minDate) ? year : year - 1,
|
||||
false
|
||||
)
|
||||
"
|
||||
>
|
||||
<AtomsIconsArrowRightLine
|
||||
:size="16"
|
||||
:color="isDisabled(false) ? '#CCCCCC' : '#333333'"
|
||||
class="relative rotate-180"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center gap-[8px] w-auto h-[32px]"
|
||||
@click="handleOpenYearPicker"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center justify-center text-[#1F1F1F] text-[18px] font-[500] leading-[26px] tracking-[-0.54px]"
|
||||
>
|
||||
{{ years.find(y => y.value === year)?.text }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="isDisabled(true)"
|
||||
@click="
|
||||
updateMonthYear(
|
||||
month,
|
||||
year === getYear(maxDate) ? year : year + 1,
|
||||
false
|
||||
)
|
||||
"
|
||||
>
|
||||
<AtomsIconsArrowRightLine
|
||||
:size="16"
|
||||
:color="isDisabled(true) ? '#CCCCCC' : '#333333'"
|
||||
class="relative"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-[4px] w-full">
|
||||
<button
|
||||
v-for="month in generateMonthList()"
|
||||
:key="month"
|
||||
type="button"
|
||||
:class="[
|
||||
'inline-flex items-center justify-center w-full h-[52px] px-[12px] py-[8px] text-center text-[14px] font-[400] leading-[20px] tracking-[-0.42px] rounded-[4px] transition-colors',
|
||||
year === currentYear && month === currentMonth
|
||||
? 'bg-[#FC4420] text-white'
|
||||
: 'text-[#333333] hover:bg-[#F5F5F5]',
|
||||
]"
|
||||
@click="handleMonthSelect(month)"
|
||||
>
|
||||
{{ months.find(m => m.value === month)?.text }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="isShowYearPicker && isMenuOpened"
|
||||
class="absolute z-[2] top-0 left-0 w-full h-full mt-[8px] overflow-y-auto bg-white"
|
||||
>
|
||||
<div class="grid grid-cols-4 gap-[4px]">
|
||||
<button
|
||||
v-for="year in generateYearList()"
|
||||
:key="year"
|
||||
type="button"
|
||||
:class="[
|
||||
'px-[12px] py-[8px] text-center text-[14px] font-[400] leading-[20px] tracking-[-0.42px] rounded-[4px] transition-colors',
|
||||
year === currentYear
|
||||
? 'bg-[#FC4420] text-white'
|
||||
: 'text-[#333333] hover:bg-[#F5F5F5]',
|
||||
]"
|
||||
@click="handleYearSelect(year)"
|
||||
>
|
||||
{{ years.find(y => y.value === year)?.text }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</VueDatePicker>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.date-picker-wrap :deep(.dp__calendar_header_separator) {
|
||||
display: none !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__menu) {
|
||||
padding: 0 12px 8px !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__menu_inner) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__calendar) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__calendar_row) {
|
||||
-webkit-gap: 4px !important;
|
||||
-moz-gap: 4px !important;
|
||||
-o-gap: 4px !important;
|
||||
-ms-gap: 4px !important;
|
||||
gap: 4px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__calendar_item) {
|
||||
padding: 2px 4px !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__cell_inner) {
|
||||
padding: 8px !important;
|
||||
border-radius: 100% !important;
|
||||
outline: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__cell_inner.dp__date_hover:hover) {
|
||||
background-color: rgba(0, 0, 0, 0.04) !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__cell_inner.dp__today) {
|
||||
border: 1px solid rgba(0, 0, 0, 0.15) !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__cell_inner.dp__active_date) {
|
||||
background-color: #fc4420 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__cell_inner.dp__active_date span) {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__cell_disabled:hover),
|
||||
.date-picker-wrap :deep(.dp__cell_disabled) {
|
||||
cursor: default !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__menu),
|
||||
.date-picker-wrap :deep(.dp__menu_wrap),
|
||||
.date-picker-wrap :deep(.dp__overlay) {
|
||||
box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__overlay_cell_pad) {
|
||||
color: #333333 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__overlay_cell_pad:hover) {
|
||||
background-color: #f5f5f5 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__overlay_cell_pad.dp__overlay_cell_active) {
|
||||
background-color: #fc4420 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__overlay_cell_pad.dp__overlay_cell_disabled:hover),
|
||||
.date-picker-wrap :deep(.dp__overlay_cell_pad.dp__overlay_cell_disabled) {
|
||||
cursor: default !important;
|
||||
background-color: transparent !important;
|
||||
opacity: 0.35 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__inner_nav) {
|
||||
color: #333333 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__inner_nav:hover) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__inner_nav.dp__inner_nav_disabled) {
|
||||
background-color: transparent !important;
|
||||
cursor: default !important;
|
||||
opacity: 0.35 !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__month_year_select),
|
||||
.date-picker-wrap :deep(.dp__month_year_select:hover) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.date-picker-wrap :deep(.dp__arrow_bottom),
|
||||
.date-picker-wrap :deep(.dp__arrow_top),
|
||||
.date-picker-wrap :deep(.dp__arrow_left),
|
||||
.date-picker-wrap :deep(.dp__arrow_right) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.date-picker-wrap.date-picker-menu-absolute :deep(.dp__menu) {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.date-picker-wrap.date-picker-menu-fixed :deep(.dp__menu) {
|
||||
position: fixed !important;
|
||||
}
|
||||
|
||||
.date-picker-wrap :deep(.dp__month_year_select) {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
150
layers/components/blocks/Pagination.vue
Normal file
150
layers/components/blocks/Pagination.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
totalCount: number
|
||||
currentPage: number
|
||||
pageSize: number
|
||||
pageBlock?: number
|
||||
useMinMax?: boolean
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
currentPage: 1,
|
||||
pageBlock: 10,
|
||||
useMinMax: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:page'])
|
||||
|
||||
const currentPage = ref(props.currentPage)
|
||||
const firstPage = ref(1)
|
||||
const lastPage = computed(() => Math.ceil(props.totalCount / props.pageSize))
|
||||
const prevPage = computed(() => currentPage.value - 1)
|
||||
const nextPage = computed(() => currentPage.value + 1)
|
||||
|
||||
const currentBlock = computed(() =>
|
||||
isShortThanBlock.value ? 1 : Math.ceil(currentPage.value / props.pageBlock)
|
||||
)
|
||||
// 블럭 크기가 전체 페이지 수보다 작은지 여부
|
||||
const isShortThanBlock = computed(() => lastPage.value < props.pageBlock)
|
||||
// 현재 블럭이 첫 블럭인지 여부
|
||||
const isFirstBlock = computed(() => currentBlock.value === 1)
|
||||
// 현재 블럭이 마지막 블럭인지 여부
|
||||
const isLastBlock = computed(
|
||||
() => currentBlock.value === Math.ceil(lastPage.value / props.pageBlock)
|
||||
)
|
||||
|
||||
const lastBlock = computed(() =>
|
||||
isShortThanBlock.value ? 1 : Math.ceil(lastPage.value / props.pageBlock)
|
||||
)
|
||||
const currentBlockFirstPage = computed(() =>
|
||||
isShortThanBlock.value ? 1 : (currentBlock.value - 1) * props.pageBlock + 1
|
||||
)
|
||||
const blocks = computed(() => {
|
||||
if (props.totalCount === 0) {
|
||||
return []
|
||||
}
|
||||
const blockSize =
|
||||
currentBlock.value === lastBlock.value &&
|
||||
lastPage.value % props.pageBlock !== 0
|
||||
? lastPage.value - currentBlockFirstPage.value + 1
|
||||
: props.pageBlock
|
||||
|
||||
return Array.from(
|
||||
{ length: blockSize },
|
||||
(_, i) => currentBlockFirstPage.value + i
|
||||
)
|
||||
})
|
||||
|
||||
const handlePagination = (page: number) => {
|
||||
currentPage.value = page
|
||||
emits('update:page', page)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log(blocks.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="props.totalCount > 0"
|
||||
class="relative flex items-center justify-center w-full py-[32px]"
|
||||
>
|
||||
<template v-if="totalCount > pageSize">
|
||||
<template v-if="props.useMinMax">
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="currentPage === firstPage"
|
||||
@click="handlePagination(firstPage)"
|
||||
>
|
||||
<AtomsIconsArrowDoubleLeftLine
|
||||
:size="11"
|
||||
:color="currentPage === firstPage ? '#CCCCCC' : '#333333'"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="currentPage === firstPage"
|
||||
@click="handlePagination(prevPage)"
|
||||
>
|
||||
<AtomsIconsArrowRightLine
|
||||
:size="16"
|
||||
:color="currentPage === firstPage ? '#CCCCCC' : '#333333'"
|
||||
class="rotate-180"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<ol class="relative inline-flex items-center justify-center">
|
||||
<template v-for="page in blocks" :key="page">
|
||||
<li
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
>
|
||||
<AtomsButton
|
||||
type="action"
|
||||
button-size="size-small"
|
||||
background-color="transparent"
|
||||
text-color="#333333"
|
||||
:class="[
|
||||
'!w-full !h-full p-0 rounded-full text-center text-[14px] font-[500] leading-[24px] tracking-[-0.42px]',
|
||||
page === currentPage
|
||||
? '!bg-[#C7AE8B] !text-white cursor-default'
|
||||
: '',
|
||||
]"
|
||||
@click="handlePagination(page)"
|
||||
>
|
||||
<span>{{ page }}</span>
|
||||
</AtomsButton>
|
||||
</li>
|
||||
</template>
|
||||
</ol>
|
||||
<template v-if="totalCount > pageSize">
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="currentPage === lastPage"
|
||||
@click="handlePagination(nextPage)"
|
||||
>
|
||||
<AtomsIconsArrowRightLine
|
||||
:size="16"
|
||||
:color="currentPage === lastPage ? '#CCCCCC' : '#333333'"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="relative inline-flex items-center justify-center w-[32px] h-[32px]"
|
||||
:disabled="currentPage === lastPage"
|
||||
@click="handlePagination(lastPage)"
|
||||
>
|
||||
<AtomsIconsArrowDoubleLeftLine
|
||||
:size="11"
|
||||
:color="currentPage === lastPage ? '#CCCCCC' : '#333333'"
|
||||
class="rotate-180"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -43,11 +43,11 @@ const componentProps = computed(() => {
|
||||
<template>
|
||||
<div class="flex flex-wrap items-end justify-between mb-[16px] md:mb-[24px]">
|
||||
<h3
|
||||
class="text-[#1F1F1F] text-[18px] font-bold leading-[26px] tracking-[-0.54px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px]"
|
||||
class="text-[#1F1F1F] text-[18px] font-bold leading-[26px] tracking-[-0.54px] md:text-[24px] md:leading-[34px] md:tracking-[0.72px] shrink-0"
|
||||
>
|
||||
<span>{{ props.title }}</span>
|
||||
</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<slot />
|
||||
<p
|
||||
v-if="props.description && !props.link"
|
||||
|
||||
Reference in New Issue
Block a user