Целевая страница отсканированного QR-кода (пока без реальной отправки сообщения)
Main daploy / deploy (push) Successful in 37s
Main daploy / deploy (push) Successful in 37s
This commit is contained in:
+10
-2
@@ -43,5 +43,13 @@ export const createQrCodeDocument = async (
|
|||||||
return qrCodeDoc
|
return qrCodeDoc
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getQrCodeDocument = (uri: string) =>
|
export const getQrCodeDocument = (uri: string) => {
|
||||||
qrCodeDbLocal.get<QRCodeDocument>(`qr_code:${uri}`)
|
try {
|
||||||
|
return qrCodeDbLocal.get<QRCodeDocument>(`qr_code:${uri}`)
|
||||||
|
} catch (e) {
|
||||||
|
if ((e as { code: string }).code === 'not_found') {
|
||||||
|
return qrCodeDbRemote.get<QRCodeDocument>(`qr_code:${uri}`)
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<h2 class="text-xl font-semibold mb-4">Выберите доступные действия</h2>
|
<h2 class="text-xl font-semibold mb-4">Выберите доступные действия</h2>
|
||||||
<p class="text-gray-600 mb-4">Укажите, какие действия будут доступны для сканирующих QR-код.</p>
|
<p class="text-gray-600 mb-4">Укажите, какие действия будут доступны для сканирующих QR‑код.</p>
|
||||||
<p class="text-sm text-gray-500 mb-6">Вы сможете изменить эти настройки позже.</p>
|
<p class="text-sm text-gray-500 mb-6">Вы сможете изменить эти настройки позже.</p>
|
||||||
<div class="flex flex-col gap-3 mb-4">
|
<div class="flex flex-col gap-3 mb-4">
|
||||||
<div v-for="(label, action) in actionsOptions" :key="action" class="flex items-center gap-2">
|
<div v-for="(label, action) in actionsOptions" :key="action" class="flex items-center gap-2">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2 class="text-xl font-semibold mb-4">Сообщения для отправки</h2>
|
<h2 class="text-xl font-semibold mb-4">Сообщения для отправки</h2>
|
||||||
<p class="text-gray-600 mb-4">
|
<p class="text-gray-600 mb-4">
|
||||||
После считывания QR-кода можно будет выбрать одно из этих сообщений или написать своё.
|
После считывания QR‑кода можно будет выбрать одно из этих сообщений или написать своё.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm text-gray-500 mb-6">Вы сможете изменить эти сообщения позже.</p>
|
<p class="text-sm text-gray-500 mb-6">Вы сможете изменить эти сообщения позже.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<h2 class="text-xl font-semibold mb-4">Где вы хотите разместить QR-код?</h2>
|
<h2 class="text-xl font-semibold mb-4">Где вы хотите разместить QR‑код?</h2>
|
||||||
<p class="text-gray-600 mb-4">Этот выбор поможет настроить QR-код с подходящими действиями.</p>
|
<p class="text-gray-600 mb-4">Этот выбор поможет настроить QR‑код с подходящими действиями.</p>
|
||||||
<div class="flex flex-col gap-3 mb-4">
|
<div class="flex flex-col gap-3 mb-4">
|
||||||
<div v-for="(label, type) in qrCodePlacements" :key="type" class="flex items-center gap-2">
|
<div v-for="(label, type) in qrCodePlacements" :key="type" class="flex items-center gap-2">
|
||||||
<RadioButton
|
<RadioButton
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<h2 class="text-xl font-semibold mb-4">Введите название для вашего QR-кода</h2>
|
<h2 class="text-xl font-semibold mb-4">Введите название для вашего QR‑кода</h2>
|
||||||
<p class="text-gray-600 mb-4">Название поможет вам организовать ваши QR-коды.</p>
|
<p class="text-gray-600 mb-4">Название поможет вам организовать ваши QR‑коды.</p>
|
||||||
|
|
||||||
<form @submit.prevent="saveQRCode">
|
<form @submit.prevent="saveQRCode">
|
||||||
<label for="name" class="block text-gray-700 font-semibold mb-2">Название</label>
|
<label for="name" class="block text-gray-700 font-semibold mb-2">Название</label>
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
label="Создать QR-код"
|
label="Создать QR‑код"
|
||||||
class="p-button-md mt-4 mb-4"
|
class="p-button-md mt-4 mb-4"
|
||||||
:disabled="!name"
|
:disabled="!name"
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
<p v-if="error" class="text-red-500 mt-4">Ошибка создания QR-кода: {{ error }}</p>
|
<p v-if="error" class="text-red-500 mt-4">Ошибка создания QR‑кода: {{ error }}</p>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -17,17 +17,25 @@ const routes = [
|
|||||||
path: '/manage/:qr_code_uri/ready',
|
path: '/manage/:qr_code_uri/ready',
|
||||||
name: 'ManageQRCodeReady',
|
name: 'ManageQRCodeReady',
|
||||||
component: () => import('@/views/ManageQRCodeReadyView.vue'),
|
component: () => import('@/views/ManageQRCodeReadyView.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: '/read/:qr_code_uri',
|
||||||
|
name: 'ReadQRCode',
|
||||||
|
component: () => import('@/views/ReadQRCodeView.vue'),
|
||||||
|
props: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// path: '/create/actions',
|
// path: '/create/actions',
|
||||||
// name: 'create-actions',
|
// name: 'create-actions',
|
||||||
// component: () => import('@/views/CreateActionsView.vue'), // Второй шаг создания QR-кода
|
// component: () => import('@/views/CreateActionsView.vue'), // Второй шаг создания QR‑кода
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: '/manage/:qr_code/settings',
|
// path: '/manage/:qr_code/settings',
|
||||||
// name: 'manage-settings',
|
// name: 'manage-settings',
|
||||||
// component: () => import('@/views/SettingsView.vue'), // Настройки QR-кода для владельца
|
// component: () => import('@/views/SettingsView.vue'), // Настройки QR‑кода для владельца
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: '/manage/profile',
|
// path: '/manage/profile',
|
||||||
@@ -47,7 +55,7 @@ const routes = [
|
|||||||
// {
|
// {
|
||||||
// path: '/:qr_code',
|
// path: '/:qr_code',
|
||||||
// name: 'public-qr',
|
// name: 'public-qr',
|
||||||
// component: () => import('@/views/PublicQRView.vue'), // Публичная страница QR-кода
|
// component: () => import('@/views/PublicQRView.vue'), // Публичная страница QR‑кода
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// path: '/help',
|
// path: '/help',
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import { type BaseDocument } from './BaseDocument'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Интерфейс для документа сообщения.
|
* Интерфейс для документа сообщения.
|
||||||
* Используется для хранения сообщений, отправленных через QR-код, с поддержкой вложений.
|
* Используется для хранения сообщений, отправленных через QR‑код, с поддержкой вложений.
|
||||||
*/
|
*/
|
||||||
export interface MessageDocument extends BaseDocument {
|
export interface MessageDocument extends BaseDocument {
|
||||||
/** Тип документа, всегда 'message' */
|
/** Тип документа, всегда 'message' */
|
||||||
type: 'message'
|
type: 'message'
|
||||||
|
|
||||||
/** Идентификатор QR-кода, к которому относится сообщение */
|
/** Идентификатор QR‑кода, к которому относится сообщение */
|
||||||
qr_code: string
|
qr_code: string
|
||||||
|
|
||||||
/** Идентификатор чата для связывания сообщений */
|
/** Идентификатор чата для связывания сообщений */
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { BaseDocument } from './BaseDocument'
|
import type { BaseDocument } from './BaseDocument'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Интерфейс для документа QR-кода.
|
* Интерфейс для документа QR‑кода.
|
||||||
* Представляет QR-код, созданный пользователем, и информацию о привязанном объекте.
|
* Представляет QR‑код, созданный пользователем, и информацию о привязанном объекте.
|
||||||
*/
|
*/
|
||||||
export interface QRCodeDocument extends BaseDocument {
|
export interface QRCodeDocument extends BaseDocument {
|
||||||
/** Тип документа, всегда 'qr_code' */
|
/** Тип документа, всегда 'qr_code' */
|
||||||
@@ -11,18 +11,18 @@ export interface QRCodeDocument extends BaseDocument {
|
|||||||
/** Уникальный URL для настройки и взаимодействия */
|
/** Уникальный URL для настройки и взаимодействия */
|
||||||
uri: string
|
uri: string
|
||||||
|
|
||||||
/** Ссылка на QR-код */
|
/** Ссылка на QR‑код */
|
||||||
url: string
|
url: string
|
||||||
|
|
||||||
/** Идентификатор пользователя, который создал QR-код */
|
/** Идентификатор пользователя, который создал QR‑код */
|
||||||
user_uuid: string
|
user_uuid: string
|
||||||
|
|
||||||
/** Название объекта, для которого создан QR-код, например "Мой автомобиль" */
|
/** Название объекта, для которого создан QR‑код, например "Мой автомобиль" */
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
deliveryMethod: 'telegram' | 'webPushSubscription'
|
deliveryMethod: 'telegram' | 'webPushSubscription'
|
||||||
|
|
||||||
/** Тип объекта, для которого создан QR-код, например "car" */
|
/** Тип объекта, для которого создан QR‑код, например "car" */
|
||||||
placement: 'car' | 'pet' | 'door' | 'store' | 'restaurant_table' | 'other'
|
placement: 'car' | 'pet' | 'door' | 'store' | 'restaurant_table' | 'other'
|
||||||
|
|
||||||
/** Доступные действия для гостей (например, ["send_message"]) */
|
/** Доступные действия для гостей (например, ["send_message"]) */
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-lg mx-auto py-6 px-4 md:px-6">
|
<div class="max-w-lg mx-auto py-6 px-4 md:px-6">
|
||||||
<section class="content text-center">
|
<section class="content text-center">
|
||||||
<h2 class="text-xl font-semibold mb-4">Ваш QR-код готов</h2>
|
<h2 class="text-xl font-semibold mb-4">Ваш QR‑код готов</h2>
|
||||||
<p class="text-gray-600 mb-4">Сохраните его и ознакомьтесь с инструкциями по размещению.</p>
|
<p class="text-gray-600 mb-4">Сохраните его и ознакомьтесь с инструкциями по размещению.</p>
|
||||||
|
|
||||||
<!-- Рендер QR-кода -->
|
<!-- Рендер QR‑кода -->
|
||||||
<div v-if="!loading && svgUrl" class="border p-4 bg-white rounded-lg mb-6">
|
<div v-if="!loading && svgUrl" class="border p-4 bg-white rounded-lg mb-6">
|
||||||
<img :src="svgUrl" alt="QR-код" />
|
<img :src="svgUrl" alt="QR‑код" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Сообщение об ошибке -->
|
<!-- Сообщение об ошибке -->
|
||||||
@@ -37,14 +37,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import QRCodeStyling from 'qr-code-styling'
|
import QRCodeStyling from 'qr-code-styling'
|
||||||
import { getQrCodeDocument } from '@/api/qrCode'
|
import { getQrCodeDocument } from '@/api/qrCode'
|
||||||
import { optimize } from 'svgo'
|
import { optimize } from 'svgo'
|
||||||
|
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
||||||
|
|
||||||
// Получаем параметры маршрута
|
const { qr_code_uri } = defineProps<{ qr_code_uri: QRCodeDocument['uri'] }>()
|
||||||
const route = useRoute()
|
|
||||||
const { qr_code_uri } = route.params as { qr_code_uri: string }
|
|
||||||
|
|
||||||
// Локальные состояния
|
// Локальные состояния
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@@ -94,7 +92,7 @@ async function generateOptimizedSvg(url: string) {
|
|||||||
throw new Error('Ошибка оптимизации SVG')
|
throw new Error('Ошибка оптимизации SVG')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Ошибка генерации QR-кода')
|
throw new Error('Ошибка генерации QR‑кода')
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = (err as Error).message || 'Неизвестная ошибка'
|
error.value = (err as Error).message || 'Неизвестная ошибка'
|
||||||
@@ -109,19 +107,19 @@ async function downloadPng() {
|
|||||||
extension: 'png',
|
extension: 'png',
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = (err as Error).message || 'Ошибка при скачивании QR-кода'
|
error.value = (err as Error).message || 'Ошибка при скачивании QR‑кода'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Монтирование
|
// Монтирование
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
// Загружаем данные QR-кода
|
// Загружаем данные QR‑кода
|
||||||
const doc = await getQrCodeDocument(qr_code_uri)
|
const doc = await getQrCodeDocument(qr_code_uri)
|
||||||
qrCodeData.value = { name: doc.name, url: doc.url }
|
qrCodeData.value = { name: doc.name, url: doc.url }
|
||||||
await generateOptimizedSvg(doc.url)
|
await generateOptimizedSvg(doc.url)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = (err as Error).message || 'Ошибка загрузки данных QR-кода'
|
error.value = (err as Error).message || 'Ошибка загрузки данных QR‑кода'
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col min-h-screen">
|
||||||
|
<!-- Основной контент -->
|
||||||
|
<main class="flex-grow flex items-center justify-center py-6 px-4 md:px-6">
|
||||||
|
<section class="max-w-lg w-full">
|
||||||
|
<p class="text-gray-600 mb-4 text-center">
|
||||||
|
Выберите сообщение из списка или напишите своё:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Предустановленные сообщения -->
|
||||||
|
<div v-if="presetMessages.length" class="mb-6">
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<Button
|
||||||
|
v-for="(message, index) in presetMessages"
|
||||||
|
:key="index"
|
||||||
|
class="p-button-outlined w-full text-left"
|
||||||
|
:disabled="loading"
|
||||||
|
@click="sendPresetMessage(message)"
|
||||||
|
>
|
||||||
|
{{ message }}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Текстовое поле ввода с кнопкой -->
|
||||||
|
<form @submit.prevent="sendCustomMessage" class="flex gap-2 items-center">
|
||||||
|
<InputText
|
||||||
|
id="customMessage"
|
||||||
|
v-model="customMessage"
|
||||||
|
class="flex-1"
|
||||||
|
placeholder="Напишите сообщение"
|
||||||
|
:disabled="loading"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
icon="pi pi-send"
|
||||||
|
class="p-button-md"
|
||||||
|
:disabled="!customMessage.trim() || loading"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p v-if="success" class="text-green-500 mt-4 text-center">Сообщение отправлено!</p>
|
||||||
|
<p v-if="error" class="text-red-500 mt-4 text-center">Ошибка: {{ error }}</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Футер -->
|
||||||
|
<footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">
|
||||||
|
<p>
|
||||||
|
Нужен такой же?
|
||||||
|
<a href="/create" class="underline">Создайте свой QR‑код</a>.
|
||||||
|
</p>
|
||||||
|
<p class="mt-1">
|
||||||
|
Сделайте НаСвязи удобнее. Будем рады вашей <a href="/support" class="underline"
|
||||||
|
>поддержке</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { getQrCodeDocument } from '@/api/qrCode'
|
||||||
|
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
||||||
|
|
||||||
|
const { qr_code_uri } = defineProps<{ qr_code_uri: QRCodeDocument['uri'] }>()
|
||||||
|
|
||||||
|
// Локальные состояния
|
||||||
|
const presetMessages = ref<string[]>([])
|
||||||
|
const customMessage = ref('')
|
||||||
|
const success = ref(false)
|
||||||
|
const error = ref<string | null>(null)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
// Функция для отправки сообщения
|
||||||
|
async function sendMessage(message: string) {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
console.log({
|
||||||
|
qr_code_uri,
|
||||||
|
message: message.trim(),
|
||||||
|
})
|
||||||
|
|
||||||
|
success.value = true
|
||||||
|
error.value = null
|
||||||
|
customMessage.value = ''
|
||||||
|
} catch {
|
||||||
|
success.value = false
|
||||||
|
error.value = 'Ошибка отправки сообщения'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для отправки предустановленного сообщения
|
||||||
|
async function sendPresetMessage(message: string) {
|
||||||
|
await sendMessage(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для отправки пользовательского сообщения
|
||||||
|
async function sendCustomMessage() {
|
||||||
|
await sendMessage(customMessage.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка данных QR‑кода
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const qrCodeDoc = await getQrCodeDocument(qr_code_uri)
|
||||||
|
presetMessages.value = qrCodeDoc!.predefinedMessage || []
|
||||||
|
} catch {
|
||||||
|
error.value = 'Ошибка загрузки данных QR‑кода'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user