карта ПВЗ
All checks were successful
Deploy / build (push) Successful in 2m21s

This commit is contained in:
alsaze
2025-10-17 17:10:14 +03:00
parent 887ea75e8b
commit 2b8a5e5774
10 changed files with 418 additions and 95 deletions

View File

@@ -1,6 +1,5 @@
<template>
<div class="cart">
</pre>
<div class="cart__items">
<div
v-for="cartItem in cart?.line_items"

View File

@@ -1,97 +1,197 @@
<template>
<div class="contacts">
<UInput
v-model="name"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
class="relative"
>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
name
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
<form class="contacts" @submit.prevent="onSubmit">
<div>
<UInput
id="name"
v-model="name"
v-bind="nameAttrs"
name="name"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
:invalid="Boolean(errors.name)"
:help="errors.name"
class="relative w-100"
:color="errors.name ? 'error' : 'neutral'"
highlight
autofocus
>
<span class="inline-flex bg-default px-1">Имя</span>
</label>
</UInput>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
name
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
>
<span class="inline-flex bg-default px-1">Имя</span>
</label>
</UInput>
<UInput
v-model="surname"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
class="relative"
>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
surname
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
>
<span class="inline-flex bg-default px-1">Фамилия</span>
</label>
</UInput>
<div v-if="errors?.name" style="color: #ff6467">
{{ errors?.name }}
</div>
</div>
<UInput
v-model="phone"
v-maska="'+7 (###) ###-##-##'"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
class="relative"
>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
phone
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
<div>
<UInput
id="surname"
v-model="surname"
v-bind="surnameAttrs"
name="surname"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
:invalid="Boolean(errors.surname)"
:help="errors.surname"
class="relative w-100"
:color="errors.surname ? 'error' : 'neutral'"
highlight
>
<span class="inline-flex bg-default px-1">телефон</span>
</label>
</UInput>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
surname
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
>
<span class="inline-flex bg-default px-1">Фамилия</span>
</label>
</UInput>
<UInput
v-model="email"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
class="relative"
trailing-icon="i-lucide-at-sign"
>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
email
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
<div v-if="errors?.surname" style="color: #ff6467">
{{ errors?.surname }}
</div>
</div>
<div>
<UInput
id="phone"
v-model="phone"
v-bind="phoneAttrs"
v-maska="'+7 (###) ###-##-##'"
name="phone"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
:invalid="Boolean(errors.phone)"
:help="errors.phone"
class="relative w-100"
:color="errors.phone ? 'error' : 'neutral'"
highlight
>
<span class="inline-flex bg-default px-1">email</span>
</label>
</UInput>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
phone
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
>
<span class="inline-flex bg-default px-1">телефон</span>
</label>
</UInput>
<div v-if="errors?.phone" style="color: #ff6467">
{{ errors?.phone }}
</div>
</div>
<div>
<UInput
id="email"
v-model="email"
v-bind="emailAttrs"
name="email"
size="xl"
placeholder=" "
:ui="{ base: 'peer' }"
class="relative w-100"
trailing-icon="i-lucide-at-sign"
:invalid="Boolean(errors.email)"
:help="errors.email"
:color="errors.email ? 'error' : 'neutral'"
highlight
>
<label
class="pointer-events-none absolute left-3 text-base transition-all peer-focus:-top-3 peer-focus:text-highlighted peer-focus:text-sm peer-focus:font-medium" :class="[
email
? '-top-3 text-sm text-highlighted font-medium'
: 'top-3 text-dimmed peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-placeholder-shown:text-dimmed',
]"
>
<span class="inline-flex bg-default px-1">email</span>
</label>
</UInput>
<div v-if="errors?.email" style="color: #ff6467">
{{ errors?.email }}
</div>
</div>
<UButton
size="xl"
label="продолжить"
class="justify-center text-center"
@click="nextStep"
type="submit"
:disabled="errors && Object.keys(errors).length > 0"
/>
</div>
</form>
</template>
<script lang="ts" setup>
import { useCheckout } from '../../composables/useCheckout'
const name = ref('')
const surname = ref('')
const phone = ref('')
const email = ref('')
const { setContacts, nextStep } = useCheckout()
const { nextStep } = useCheckout()
const { errors, handleSubmit, defineField } = useForm({
initialValues: {
name: '',
surname: '',
phone: '',
email: '',
},
validationSchema: {
name(value: string) {
if (!value)
return 'Укажите имя'
if (value.trim().length < 2)
return 'Минимум 2 символа'
return true
},
surname(value: string) {
if (!value)
return 'Укажите фамилию'
if (value.trim().length < 2)
return 'Минимум 2 символа'
return true
},
phone(value: string) {
if (!value)
return 'Укажите телефон'
const digits = (value || '').replace(/\D/g, '')
if (digits.length < 11)
return 'Введите корректный телефон'
return true
},
email(value: string) {
if (!value)
return 'Укажите email'
const re = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/
if (!re.test(value))
return 'Введите корректный email'
return true
},
},
})
const [name, nameAttrs] = defineField('name')
const [surname, surnameAttrs] = defineField('surname')
const [phone, phoneAttrs] = defineField('phone')
const [email, emailAttrs] = defineField('email')
const onSubmit = handleSubmit((values) => {
setContacts(values)
nextStep()
})
definePageMeta({
layout: 'checkout',

View File

@@ -1,12 +1,119 @@
<template>
<UButton label="СОздать ОредЕр" @click="createOrder" />
<div class="summary">
<div class="summary__info">
<h3>в заказе &nbsp;&bull;&nbsp; {{ cart?.line_items?.length }} шт</h3>
<div class="summary__items">
<div
v-for="cartItem in cart?.line_items"
:key="cartItem.variation_id"
>
<SummaryCartItem :summary-cart-item="cartItem" />
</div>
</div>
<div class="space-y-2">
<h3 class="d-flex gap-1 text-center align-items-center">
Yandex
<UButton
size="sm"
class="text-muted-foreground"
variant="ghost"
icon="i-ph-pencil-simple-line"
@click="onEditDelivery"
/>
</h3>
<div class="flex items-center gap-2">
<UIcon name="i-ph-user" class="text-muted-foreground" />
<span>{{ contacts?.name }} {{ contacts?.surname }}</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-ph-user" class="text-muted-foreground" />
<span>{{ contacts?.name }} {{ contacts?.surname }}</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-ph-user" class="text-muted-foreground" />
<span>{{ contacts?.name }} {{ contacts?.surname }}</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-ph-user" class="text-muted-foreground" />
<span>{{ contacts?.name }} {{ contacts?.surname }}</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-ph-user" class="text-muted-foreground" />
<span>{{ contacts?.name }} {{ contacts?.surname }}</span>
</div>
</div>
<div class="space-y-2">
<h3 class="d-flex gap-1 text-center align-items-center">
Получатель
<UButton
size="sm"
class="text-muted-foreground"
variant="ghost"
icon="i-ph-pencil-simple-line"
@click="onEditContacts"
/>
</h3>
<div class="flex items-center gap-2">
<UIcon name="i-ph-user" class="text-muted-foreground" />
<span>{{ contacts?.name }} {{ contacts?.surname }}</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-ph-envelope-simple" class="text-muted-foreground" />
<span>{{ contacts?.email }}</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-ph-phone" class="text-muted-foreground" />
<span>{{ contacts?.phone }}</span>
</div>
</div>
</div>
<div>
<UButton label="СОздать ОредЕр" @click="createOrder" />
</div>
</div>
</template>
<script setup lang="ts">
import type { IBspb } from '~/server/shared/types/bspb'
import SummaryCartItem from '../../components/cart/SummaryCartItem.vue'
const { cart } = useCart()
const { contacts } = useCheckout()
function onEditDelivery() {
console.log('pizda')
}
function onEditContacts() {
console.log('pizda')
}
const createOrder = async () => {
const { data } = await useFetch<IBspb>('/api/bspb')
const { data } = await useFetch<IBspb>('/api/bspb', {
method: 'POST',
body: {
order: {
typeRid: 'Purchase',
amount: 100,
currency: 'RUB',
hppRedirectUrl: `${import.meta.env.VITE_BASE_URL}/cart`,
},
},
})
const redirectUrl = `${data?.value?.order?.hppUrl}?orderId=${data?.value?.order?.id}&password=${data.value?.order?.password}`
window.open(redirectUrl, '_blank')
@@ -17,6 +124,27 @@ definePageMeta({
})
</script>
<style scoped lang="scss">
<style lang="scss">
@use '~/assets/scss/utils' as *;
.summary {
max-width: 1200px;
margin-inline: auto;
padding-inline: 16px;
display: flex;
flex-direction: row;
justify-content: space-between;
&__info {
display: flex;
flex-direction: column;
gap: 16px;
}
&__items {
display: flex;
flex-direction: row;
gap: 4px;
}
}
</style>