| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- <template>
- <div class="p-field mb-3 inline-block w-full align-top">
- <label class="p-field_label">{{ label }}</label>
- <div class="relative">
- <select
- v-model="inputValue"
- v-if="type === 'country'"
- class="p-field_input"
- >
- <option>中国</option>
- </select>
- <input
- v-else
- class="p-field_input"
- :class="{ invalid }"
- :name="name"
- :placeholder="placeholder"
- :type="inputType"
- :inputmode="inputMode"
- @blur="onBlur"
- v-model="inputValue"
- @input="onInput"
- />
- <div
- class="absolute flex items-center py-3 pr-3 top-0 right-0 bottom-0 overflow-visible"
- v-if="type === 'card'"
- >
- <div class="card-icon-container card-icon-more">
- <template v-if="cardIcon">
- <img :src="cardIcon" class="card-icon" />
- </template>
- <template v-else>
- <Transition name="icon-fade">
- <img
- :src="IconVisa"
- class="card-icon"
- v-if="moreIcon === 1"
- />
- </Transition>
- <Transition name="icon-fade">
- <img
- :src="IconMaster"
- class="card-icon"
- v-if="moreIcon === 2"
- />
- </Transition>
- <Transition name="icon-fade">
- <img
- :src="IconAmex"
- class="card-icon"
- v-if="moreIcon === 3"
- />
- </Transition>
- <Transition name="icon-fade">
- <img
- :src="IconDiscover"
- class="card-icon"
- v-if="moreIcon === 4"
- />
- </Transition>
- <Transition name="icon-fade">
- <img
- :src="IconDiners"
- class="card-icon"
- v-if="moreIcon === 5"
- />
- </Transition>
- <Transition name="icon-fade">
- <img
- :src="IconJcb"
- class="card-icon"
- v-if="moreIcon === 6"
- />
- </Transition>
- </template>
- </div>
- </div>
- </div>
- <p class="p-field_msg" role="alert" v-if="invalid">{{ message }}</p>
- </div>
- </template>
- <script setup>
- import { computed, onBeforeUnmount, onMounted, ref } from "vue";
- import IconVisa from "@/assets/visa.svg";
- import IconMaster from "@/assets/master.svg";
- import IconAmex from "@/assets/amex.svg";
- import IconDiscover from "@/assets/discover.svg";
- import IconDiners from "@/assets/diners.svg";
- import IconJcb from "@/assets/jcb.svg";
- import cardValidator from "card-validator";
- import creditCardType from "credit-card-type";
- const moreIcon = ref(1);
- const t = setInterval(() => {
- moreIcon.value = (moreIcon.value % 6) + 1;
- }, 5000);
- onBeforeUnmount(() => {
- clearInterval(t);
- });
- const props = defineProps({
- type: String,
- placeholder: {
- type: String,
- default: "",
- },
- name: {
- type: String,
- default: "",
- },
- label: {
- type: String,
- default: "",
- },
- });
- const inputType = computed(() => {
- switch (props.type) {
- default:
- return "text";
- }
- });
- const inputMode = computed(() => {
- switch (props.type) {
- case "card":
- return "numeric";
- default:
- return "text";
- }
- });
- const inputValue = ref("");
- const card = computed(() => cardValidator.number(inputValue.value));
- const cardIcon = computed(() => {
- if (card.value?.card) {
- switch (card.value.card.type) {
- case "visa":
- return IconVisa;
- case "mastercard":
- return IconMaster;
- case "amex":
- return IconAmex;
- case "discover":
- return IconDiscover;
- case "diners":
- return IconDiners;
- case "jcb":
- return IconJcb;
- }
- }
- return null;
- });
- const message = ref("");
- const invalid = computed(() => message.value !== "");
- function onBlur() {}
- function vlidate() {
- if (props.type === "card") {
- if (!card.value?.isValid) {
- message.value = "Invalid card number";
- } else {
- message.value = "";
- }
- } else {
- message.value = "";
- }
- }
- function onInput(e) {
- message.value = "";
- if (props.type === "card") {
- inputValue.value = inputValue.value
- .replace(/\D/g, "")
- .substring(0, 19)
- .replace(/(.{4})/g, "$1 ")
- .trim();
- }
- }
- defineExpose({
- validate: vlidate,
- });
- </script>
- <style lang="less" scoped>
- .p-field {
- --accessibleColorOnColorPrimary: #fff;
- --p-colorPrimaryHover: #0b81fa;
- --p-colorPrimaryActive: #228cfa;
- --p-colorPrimaryAlpha20: hsla(210, 96%, 45%, 25%);
- --p-colorPrimaryAlpha40: hsla(210, 96%, 45%, 40%);
- --p-colorPrimaryAlpha50: hsla(210, 96%, 45%, 50%);
- --p-colorPrimaryDisabled: #d9d9d9;
- --p-colorPrimaryDisabledText: #8d8d8d;
- --accessibleColorOnColorBackground: #30313d;
- --p-colorBackgroundDivider: #f2f2f2;
- --p-colorBackgroundDisabled: #f2f2f2;
- --p-colorBackgroundDisabledDeemphasize05: #e6e6e6;
- --p-colorBackgroundDeemphasize03: #f7f7f7;
- --p-colorBackgroundDeemphasize05: #f2f2f2;
- --p-colorBackgroundDeemphasize10: #e6e6e6;
- --p-colorBackgroundDeemphasize15: #d9d9d9;
- --p-colorBackgroundDeemphasize20: #cccccc;
- --p-colorBackgroundLightenAbsolute05: #ffffff;
- --p-colorBackgroundContrastAlpha05: rgba(0, 0, 0, 0.05);
- --p-colorBackgroundContrastAlpha08: rgba(0, 0, 0, 0.08);
- --p-colorBackgroundContrastAlpha30: rgba(0, 0, 0, 0.3);
- --colorTextSecondary: #6d6e78;
- --colorTextPlaceholder: #757680;
- --accessibleColorOnColorSuccess: #fff;
- --accessibleColorOnColorDanger: #fff;
- --accessibleColorOnColorWarning: #30313d;
- --p-linkProtectionsBadgeBackground: #1d3944;
- --p-linkProtectionsBadgeColor: #fff;
- --fontFamily: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
- Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
- --fontSmooth: always;
- --fontVariantLigatures: normal;
- --fontVariationSettings: normal;
- --fontLineHeight: 1.45;
- --fontSizeBase: 1rem;
- --fontSizeSm: 0.93rem;
- --fontSizeXs: 0.875rem;
- --fontSize2Xs: 0.8125rem;
- --fontSize3Xs: 0.75rem;
- --fontSizeLg: 1.0625rem;
- --fontSizeXl: 1.125rem;
- --fontSize2Xl: 1.25rem;
- --fontWeightLight: 300;
- --fontWeightNormal: 400;
- --fontWeightMedium: 600;
- --fontWeightBold: bold;
- --colorPrimary: #0570de;
- --colorBackground: #fff;
- --colorText: #30313d;
- --colorSuccess: #30b130;
- --colorDanger: #df1b41;
- --colorWarning: #f6e6b9;
- --iconColor: var(--colorTextSecondary);
- --iconHoverColor: var(--colorText);
- --iconCheckmarkColor: var(--colorPrimaryText);
- --iconCardErrorColor: var(--colorDanger);
- --iconCardCvcColor: var(--iconColor);
- --iconCardCvcErrorColor: var(--colorDanger);
- --iconChevronDownColor: var(--iconColor);
- --iconChevronDownHoverColor: var(--iconHoverColor);
- --iconCloseColor: var(--iconColor);
- --iconCloseHoverColor: var(--iconHoverColor);
- --iconLoadingIndicatorColor: var(--p-colorBackgroundContrastAlpha30);
- --iconMenuColor: var(--colorPrimary);
- --iconMenuHoverColor: var(--iconMenuColor);
- --iconMenuOpenColor: var(--iconMenuColor);
- --iconPasscodeDeviceColor: var(--iconColor);
- --iconPasscodeDeviceHoverColor: var(--iconHoverColor);
- --iconPasscodeDeviceNotificationColor: var(--colorPrimary);
- --iconRedirectColor: currentColor;
- --tabIconColor: var(--iconColor);
- --tabIconHoverColor: var(--colorText);
- --tabIconSelectedColor: var(--colorPrimary);
- --tabIconMoreColor: var(--iconColor);
- --tabIconMoreHoverColor: var(--colorText);
- --spacingUnit: 0.25rem;
- --gridRowSpacing: var(--p-spacing3);
- --gridColumnSpacing: var(--p-spacing3);
- --tabSpacing: var(--p-spacing1);
- --pickerItemSpacing: var(--p-spacing2);
- --accordionItemSpacing: var(--p-spacing2);
- --borderRadius: 5px;
- --focusBoxShadow: 0 0 0 3px var(--p-colorPrimaryAlpha20),
- 0 1px 1px 0 var(--p-colorBackgroundContrastAlpha08);
- --focusOutline: 0px;
- --spacingGridRow: var(--gridRowSpacing);
- --spacingGridColumn: var(--gridColumnSpacing);
- --spacingTab: var(--tabSpacing);
- --spacingPickerItem: var(--pickerItemSpacing);
- --spacingAccordionItem: var(--accordionItemSpacing);
- --colorPrimaryText: var(--accessibleColorOnColorPrimary);
- --colorBackgroundText: var(--accessibleColorOnColorBackground);
- --colorSuccessText: var(--accessibleColorOnColorSuccess);
- --colorDangerText: var(--accessibleColorOnColorDanger);
- --colorWarningText: var(--accessibleColorOnColorWarning);
- --colorIcon: var(--iconColor);
- --colorIconHover: var(--iconHoverColor);
- --colorIconCardError: var(--iconCardErrorColor);
- --colorIconCardCvc: var(--iconCardCvcColor);
- --colorIconCardCvcError: var(--iconCardCvcErrorColor);
- --colorIconCheckmark: var(--iconCheckmarkColor);
- --colorIconChevronDown: var(--iconChevronDownColor);
- --colorIconChevronDownHover: var(--iconChevronDownHoverColor);
- --colorIconClose: var(--iconCloseColor);
- --colorIconCloseHover: var(--iconCloseHoverColor);
- --colorIconLoadingIndicator: var(--iconLoadingIndicatorColor);
- --colorIconMenu: var(--iconMenuColor);
- --colorIconMenuHover: var(--iconMenuHoverColor);
- --colorIconMenuOpen: var(--iconMenuOpenColor);
- --colorIconPasscodeDevice: var(--iconPasscodeDeviceColor);
- --colorIconPasscodeDeviceHover: var(--iconPasscodeDeviceHoverColor);
- --colorIconPasscodeDeviceNotification: var(
- --iconPasscodeDeviceNotificationColor
- );
- --colorIconRedirect: var(--iconRedirectColor);
- --colorIconTab: var(--tabIconColor);
- --colorIconTabHover: var(--tabIconHoverColor);
- --colorIconTabSelected: var(--tabIconSelectedColor);
- --colorIconTabMore: var(--tabIconMoreColor);
- --colorIconTabMoreHover: var(--tabIconMoreHoverColor);
- --colorLogo: var(--logoColor);
- --colorLogoTab: var(--tabLogoColor);
- --colorLogoTabSelected: var(--tabLogoSelectedColor);
- --colorLogoBlock: var(--blockLogoColor);
- --p-spacingXs: 1px;
- --p-spacingSm: 2px;
- --p-spacing1: 0.25rem;
- --p-spacing2: 0.5rem;
- --p-spacing3: 0.75rem;
- --p-spacing4: 1rem;
- --p-spacing5: 1.25rem;
- --p-spacing6: 1.5rem;
- --p-spacing7: 1.75rem;
- --p-spacing8: 2rem;
- --p-spacing9: 2.25rem;
- --p-spacing10: 2.5rem;
- --p-logoLightDisplay: block;
- --p-logoDarkDisplay: none;
- --p-logoTabLightDisplay: block;
- --p-logoTabDarkDisplay: none;
- --p-logoTabSelectedLightDisplay: block;
- --p-logoTabSelectedDarkDisplay: none;
- --p-logoBlockLightDisplay: block;
- --p-logoBlockDarkDisplay: none;
- color: var(--colorText);
- line-height: 1.15;
- .p-field_label {
- margin-bottom: var(--p-spacing1);
- font-size: var(--fontSizeSm);
- transition: transform 0.5s cubic-bezier(0.19, 1, 0.22, 1),
- opacity 0.5s cubic-bezier(0.19, 1, 0.22, 1);
- display: block;
- }
- .p-field_input {
- display: block;
- width: 100%;
- padding: var(--p-spacing3);
- background-color: var(--colorBackground);
- border-radius: var(--borderRadius);
- transition: background 0.15s ease, border 0.15s ease,
- box-shadow 0.15s ease, color 0.15s ease;
- border: 1px solid var(--p-colorBackgroundDeemphasize10);
- box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.03),
- 0px 3px 6px rgba(0, 0, 0, 0.02);
- &.invalid {
- color: var(--colorDanger);
- border-color: var(--colorDanger);
- box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.03),
- 0px 3px 6px rgba(0, 0, 0, 0.02), 0 0 0 1px var(--colorDanger);
- }
- &:focus {
- outline: var(--focusOutline);
- border-color: var(--p-colorPrimaryAlpha50);
- box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.03),
- 0px 3px 6px rgba(0, 0, 0, 0.02), var(--focusBoxShadow);
- }
- }
- .p-field_msg {
- margin-top: var(--p-spacing1);
- color: var(--colorDanger);
- font-size: var(--fontSizeSm);
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
- Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
- }
- .card-icon-container {
- padding-right: 0.2em;
- }
- .card-icon {
- width: 1.95em;
- height: auto;
- transition: opacity 1.2s cubic-bezier(0.19, 1, 0.22, 1),
- transform 1.2s cubic-bezier(0.19, 1, 0.22, 1),
- -webkit-transform 1.2s cubic-bezier(0.19, 1, 0.22, 1);
- }
- .card-icon-more {
- padding-right: 0;
- position: relative;
- width: 1.95em;
- height: 100%;
- overflow: visible;
- .card-icon {
- position: absolute;
- top: 0;
- bottom: 0;
- right: 0;
- margin: auto;
- }
- }
- }
- .icon-fade-enter-from,
- .icon-fade-leave-to {
- opacity: 0;
- transform: scale(0.85);
- }
- </style>
- ./icons/CardIconDiscover.vue./icons/CardIconDiners.vue./icons/CardIconJcb.vue
|