@@ -0,0 +1,4 @@
|
||||
import PouchDB from 'pouchdb-browser'
|
||||
import PouchDBFindPlugin from 'pouchdb-find'
|
||||
|
||||
export default PouchDB.plugin(PouchDBFindPlugin)
|
||||
+12
-2
@@ -1,8 +1,18 @@
|
||||
import PouchDB from 'pouchdb-browser'
|
||||
import { type QRCodeDocument } from '@/types/DBDocumentTypes'
|
||||
import PouchDB from '@/api/PouchDB'
|
||||
|
||||
import type { QRCodeDocument, MessageDocument } from '@/types/DBDocumentTypes'
|
||||
|
||||
export const qrCodeDbRemote = new PouchDB<QRCodeDocument>('https://hereconnect.condev.ru/api/qr', {
|
||||
skip_setup: true,
|
||||
})
|
||||
|
||||
export const qrCodeDbLocal = new PouchDB<QRCodeDocument>('qr')
|
||||
|
||||
export const messagesDbLocal = new PouchDB<MessageDocument>('messages')
|
||||
|
||||
export const messagesDbRemote = new PouchDB<MessageDocument>(
|
||||
'https://hereconnect.condev.ru/api/messages',
|
||||
{
|
||||
skip_setup: true,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<!-- Список сообщений -->
|
||||
<div class="flex-grow overflow-y-auto px-4 py-2" ref="messagesContainer">
|
||||
<div
|
||||
v-for="message in messages"
|
||||
:key="message._id"
|
||||
class="mb-4 max-w-md"
|
||||
:class="{ 'ml-auto': message.from === userRole, 'mr-auto': message.from !== userRole }"
|
||||
>
|
||||
<Card
|
||||
class="p-card-sm"
|
||||
:class="{
|
||||
'bg-blue-50 border-blue-300 text-blue-500': message.from === userRole,
|
||||
'bg-gray-50 border-gray-300 text-gray-700': message.from !== userRole,
|
||||
}"
|
||||
>
|
||||
<template #content>
|
||||
{{ message.body }}
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<slot name="endOfMessages" :count="messages.length"></slot>
|
||||
</div>
|
||||
|
||||
<!-- Форма отправки сообщений -->
|
||||
<form @submit.prevent="sendMessage" class="flex gap-2 items-center" ref="form">
|
||||
<InputText
|
||||
id="newMessage"
|
||||
v-model="newMessage"
|
||||
class="flex-1"
|
||||
placeholder="Напишите сообщение"
|
||||
/>
|
||||
<Button type="submit" icon="pi pi-send" class="p-button-md" :disabled="!newMessage.trim()" />
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, useTemplateRef, watch, nextTick } from 'vue'
|
||||
import { messagesDbLocal as db, messagesDbRemote as remoteDb } from '@/api/dbs'
|
||||
import { type MessageDocument } from '@/types/DBDocumentTypes'
|
||||
|
||||
const formRef = useTemplateRef('form')
|
||||
const messagesContainerRef = useTemplateRef('messagesContainer')
|
||||
|
||||
const { qr_code_uri, chat, userRole } = defineProps<{
|
||||
qr_code_uri: MessageDocument['qr_code_uri']
|
||||
chat: MessageDocument['chat']
|
||||
userRole: MessageDocument['from']
|
||||
}>()
|
||||
|
||||
// Локальные состояния
|
||||
const messages = ref<MessageDocument[]>([])
|
||||
const newMessage = ref('')
|
||||
let changesSubscription: PouchDB.Core.Changes<MessageDocument> | null = null
|
||||
let syncHandler: PouchDB.Replication.Sync<MessageDocument> | null = null
|
||||
|
||||
// Селектор для фильтрации сообщений
|
||||
const messageSelector = {
|
||||
type: 'message',
|
||||
qr_code_uri,
|
||||
chat,
|
||||
}
|
||||
|
||||
// Загрузка сообщений
|
||||
async function loadMessages() {
|
||||
try {
|
||||
const result = await db.find({
|
||||
selector: messageSelector,
|
||||
})
|
||||
messages.value = result.docs as MessageDocument[]
|
||||
} catch (error) {
|
||||
console.error('Ошибка загрузки сообщений:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Отправка нового сообщения
|
||||
async function sendMessage() {
|
||||
const created_at = new Date().toISOString()
|
||||
const message: MessageDocument = {
|
||||
_id: `message:${qr_code_uri}:${chat}:${created_at}`,
|
||||
type: 'message',
|
||||
qr_code_uri,
|
||||
chat,
|
||||
created_at,
|
||||
from: userRole,
|
||||
body: newMessage.value.trim(),
|
||||
}
|
||||
|
||||
try {
|
||||
await db.put(message)
|
||||
newMessage.value = ''
|
||||
} catch (error) {
|
||||
console.error('Ошибка отправки сообщения:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Отслеживание изменений
|
||||
function watchChanges() {
|
||||
changesSubscription = db
|
||||
.changes({
|
||||
since: 'now',
|
||||
live: true,
|
||||
include_docs: true,
|
||||
selector: messageSelector,
|
||||
})
|
||||
.on('change', (change: { doc?: MessageDocument }) => {
|
||||
if (change.doc) {
|
||||
const index = messages.value.findIndex((m) => m._id === change.doc!._id)
|
||||
if (index === -1) {
|
||||
// Если сообщения нет, добавляем его
|
||||
messages.value.push(change.doc)
|
||||
} else {
|
||||
// Если сообщение существует, обновляем его
|
||||
messages.value[index] = change.doc
|
||||
}
|
||||
// Сортируем массив сообщений
|
||||
messages.value.sort((a, b) => (a.created_at > b.created_at ? 1 : -1))
|
||||
}
|
||||
})
|
||||
.on('error', (err) => {
|
||||
console.error('Ошибка отслеживания изменений:', err)
|
||||
})
|
||||
}
|
||||
|
||||
// Настройка синхронизации
|
||||
function setupSync() {
|
||||
syncHandler = db.sync(remoteDb, {
|
||||
live: true,
|
||||
retry: true,
|
||||
selector: messageSelector,
|
||||
})
|
||||
syncHandler.on('error', (err) => {
|
||||
console.error('Ошибка синхронизации:', err)
|
||||
})
|
||||
}
|
||||
|
||||
// Остановка синхронизации
|
||||
function stopSync() {
|
||||
if (syncHandler) {
|
||||
syncHandler.cancel()
|
||||
syncHandler = null
|
||||
}
|
||||
}
|
||||
|
||||
// Отписка от изменений
|
||||
function stopWatchingChanges() {
|
||||
if (changesSubscription) {
|
||||
changesSubscription.cancel()
|
||||
changesSubscription = null
|
||||
}
|
||||
}
|
||||
|
||||
// Логика жизненного цикла
|
||||
onMounted(async () => {
|
||||
watchChanges()
|
||||
setupSync()
|
||||
await loadMessages()
|
||||
formRef.value?.querySelector('input')?.focus()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => messages.value?.length,
|
||||
async () => {
|
||||
if (messagesContainerRef.value) {
|
||||
await nextTick()
|
||||
messagesContainerRef.value.scrollTop = messagesContainerRef.value.scrollHeight
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
stopWatchingChanges()
|
||||
stopSync()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Чат занимает всё доступное пространство */
|
||||
.flex-grow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
height: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script setup lang="ts" generic="R, E">
|
||||
import type { UseQueryReturnType } from '@tanstack/vue-query'
|
||||
|
||||
type Props = {
|
||||
query: UseQueryReturnType<R, E>
|
||||
}
|
||||
|
||||
const { query } = defineProps<Props>()
|
||||
|
||||
const { data, error, refetch, isSuccess, isError, isLoading } = query
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot v-if="isSuccess" name="default" :data="data! as R" />
|
||||
<slot v-else-if="isLoading" name="loading"><p class="text-gray-500">Загрузка данных...</p></slot>
|
||||
<slot v-else-if="isError" name="error" :error="error as E" :reload="refetch">
|
||||
Ошибка загрузки данных: {{ error && 'message' in error ? error.message : error }}
|
||||
</slot>
|
||||
</template>
|
||||
@@ -48,10 +48,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import md5 from 'md5'
|
||||
import type { ContactInfo } from '@/types/DBDocumentTypes'
|
||||
|
||||
const { contactInfo } = defineProps<{
|
||||
contactInfo: { name?: string; phone?: string; email?: string }
|
||||
}>()
|
||||
const { contactInfo } = defineProps<{ contactInfo: ContactInfo }>()
|
||||
|
||||
const success = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<section class="max-w-prose w-full">
|
||||
<p class="text-gray-600 mb-4 text-center" v-if="presetMessages.length">
|
||||
<p class="text-gray-600 mb-4 text-center" v-if="doc.predefinedMessages?.length">
|
||||
Выберите сообщение из списка или напишите своё:
|
||||
</p>
|
||||
|
||||
<!-- Предустановленные сообщения -->
|
||||
<div v-if="presetMessages.length" class="mb-6">
|
||||
<div v-if="doc.predefinedMessages?.length" class="mb-6">
|
||||
<div class="flex flex-col gap-3">
|
||||
<Button
|
||||
v-for="(message, index) in presetMessages"
|
||||
v-for="(message, index) in doc.predefinedMessages"
|
||||
:key="index"
|
||||
class="p-button-outlined w-full text-left"
|
||||
:disabled="loading"
|
||||
@@ -43,31 +43,51 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { messagesDbLocal as db } from '@/api/dbs'
|
||||
import type { MessageDocument, QRCodeDocument } from '@/types/DBDocumentTypes'
|
||||
import { ROUTE_NAMES } from '@/constants/routes'
|
||||
|
||||
const { qr_code_uri, predefinedMessages } = defineProps<{
|
||||
qr_code_uri: string
|
||||
predefinedMessages: string[]
|
||||
const { doc } = defineProps<{
|
||||
doc: QRCodeDocument
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// Локальные состояния
|
||||
const presetMessages = ref(predefinedMessages || [])
|
||||
const customMessage = ref('')
|
||||
const success = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
// Генерация идентификатора чата
|
||||
|
||||
// Функция для отправки сообщения
|
||||
async function sendMessage(message: string) {
|
||||
async function sendMessage(content: string) {
|
||||
try {
|
||||
loading.value = true
|
||||
console.log({
|
||||
qr_code_uri,
|
||||
message: message.trim(),
|
||||
})
|
||||
|
||||
const chat = (new Date().getTime() - new Date(doc.created_at).getTime()).toString(36)
|
||||
|
||||
const message: MessageDocument = {
|
||||
_id: `message:${doc.uri}:${chat}:${new Date().toISOString()}`,
|
||||
type: 'message',
|
||||
qr_code_uri: doc.uri,
|
||||
chat,
|
||||
created_at: new Date().toISOString(),
|
||||
from: 'guest',
|
||||
body: content.trim(),
|
||||
}
|
||||
|
||||
// Сохраняем сообщение в локальную базу данных
|
||||
await db.put(message)
|
||||
|
||||
success.value = true
|
||||
error.value = null
|
||||
customMessage.value = ''
|
||||
|
||||
// Перенаправляем на страницу чата
|
||||
await router.push({ name: ROUTE_NAMES.PUBLIC_CHAT, params: { qr_code_uri: doc.uri, chat } })
|
||||
} catch {
|
||||
success.value = false
|
||||
error.value = 'Ошибка отправки сообщения'
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
export const ROUTE_NAMES = {
|
||||
HOME: 'home',
|
||||
CREATE: 'create',
|
||||
MANAGE_QR_CODE_READY: 'manage-qr-code-ready',
|
||||
READ_QR_CODE: 'read-qr-code',
|
||||
DONATE: 'donate',
|
||||
PUBLIC_CHAT: 'public-chat',
|
||||
// CREATE_ACTIONS: 'create-actions',
|
||||
// MANAGE_SETTINGS: 'manage-settings',
|
||||
// MANAGE_PROFILE: 'manage-profile',
|
||||
// MANAGE_CHAT: 'manage-chat',
|
||||
// PUBLIC_QR: 'public-qr',
|
||||
// HELP: 'help',
|
||||
// ABOUT: 'about',
|
||||
// CONTACT: 'contact',
|
||||
// PRIVACY_POLICY: 'privacy-policy',
|
||||
// TERMS_OF_SERVICE: 'terms-of-service',
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { createApp } from 'vue'
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
import { createPinia } from 'pinia'
|
||||
import PouchDB from 'pouchdb-browser'
|
||||
import PouchDB from '@/api/PouchDB'
|
||||
|
||||
import PrimeVue from 'primevue/config'
|
||||
import { definePreset } from '@primevue/themes'
|
||||
|
||||
+23
-23
@@ -1,91 +1,91 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { ROUTE_NAMES } from '@/constants/routes'
|
||||
import HomeView from '@/views/HomeView.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
name: ROUTE_NAMES.HOME,
|
||||
component: HomeView,
|
||||
},
|
||||
|
||||
{
|
||||
path: '/create',
|
||||
name: ROUTE_NAMES.CREATE,
|
||||
component: () => import('@/views/CreateView.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
path: '/manage/:qr_code_uri/ready',
|
||||
name: 'ManageQRCodeReady',
|
||||
name: ROUTE_NAMES.MANAGE_QR_CODE_READY,
|
||||
component: () => import('@/views/ManageQRCodeReadyView.vue'),
|
||||
props: true,
|
||||
},
|
||||
|
||||
{
|
||||
path: '/read/:qr_code_uri',
|
||||
name: 'ReadQRCode',
|
||||
name: ROUTE_NAMES.READ_QR_CODE,
|
||||
component: () => import('@/views/ReadQRCodeView.vue'),
|
||||
props: true,
|
||||
},
|
||||
|
||||
{
|
||||
path: '/donate',
|
||||
name: 'Donate',
|
||||
name: ROUTE_NAMES.DONATE,
|
||||
component: () => import('@/views/DonateView.vue'),
|
||||
},
|
||||
{
|
||||
path: '/chat/:qr_code_uri/:chat',
|
||||
name: ROUTE_NAMES.PUBLIC_CHAT,
|
||||
component: () => import('@/views/PublicChatView.vue'),
|
||||
props: true,
|
||||
},
|
||||
|
||||
// Закомментированные маршруты
|
||||
// {
|
||||
// path: '/create/actions',
|
||||
// name: 'create-actions',
|
||||
// name: ROUTE_NAMES.CREATE_ACTIONS,
|
||||
// component: () => import('@/views/CreateActionsView.vue'), // Второй шаг создания QR‑кода
|
||||
// },
|
||||
// {
|
||||
// path: '/manage/:qr_code/settings',
|
||||
// name: 'manage-settings',
|
||||
// name: ROUTE_NAMES.MANAGE_SETTINGS,
|
||||
// component: () => import('@/views/SettingsView.vue'), // Настройки QR‑кода для владельца
|
||||
// },
|
||||
// {
|
||||
// path: '/manage/profile',
|
||||
// name: 'manage-profile',
|
||||
// name: ROUTE_NAMES.MANAGE_PROFILE,
|
||||
// component: () => import('@/views/ProfileView.vue'), // Личный кабинет владельца
|
||||
// },
|
||||
// {
|
||||
// path: '/manage/:qr_code/:chat',
|
||||
// name: 'manage-chat',
|
||||
// name: ROUTE_NAMES.MANAGE_CHAT,
|
||||
// component: () => import('@/views/ChatView.vue'), // Чат для владельца
|
||||
// },
|
||||
// {
|
||||
// path: '/chat/:qr_code/:chat',
|
||||
// name: 'public-chat',
|
||||
// component: () => import('@/views/PublicChatView.vue'), // Публичный чат для гостей
|
||||
// },
|
||||
// {
|
||||
// path: '/:qr_code',
|
||||
// name: 'public-qr',
|
||||
// name: ROUTE_NAMES.PUBLIC_QR,
|
||||
// component: () => import('@/views/PublicQRView.vue'), // Публичная страница QR‑кода
|
||||
// },
|
||||
// {
|
||||
// path: '/help',
|
||||
// name: 'help',
|
||||
// name: ROUTE_NAMES.HELP,
|
||||
// component: () => import('@/views/HelpView.vue'), // Страница помощи и FAQ
|
||||
// },
|
||||
// {
|
||||
// path: '/about',
|
||||
// name: 'about',
|
||||
// name: ROUTE_NAMES.ABOUT,
|
||||
// component: () => import('@/views/AboutView.vue'), // Страница информации о компании и проекте
|
||||
// },
|
||||
// {
|
||||
// path: '/contact',
|
||||
// name: 'contact',
|
||||
// name: ROUTE_NAMES.CONTACT,
|
||||
// component: () => import('@/views/ContactView.vue'), // Страница с контактной информацией
|
||||
// },
|
||||
// {
|
||||
// path: '/privacy-policy',
|
||||
// name: 'privacy-policy',
|
||||
// name: ROUTE_NAMES.PRIVACY_POLICY,
|
||||
// component: () => import('@/views/PrivacyPolicyView.vue'), // Политика конфиденциальности
|
||||
// },
|
||||
// {
|
||||
// path: '/terms-of-service',
|
||||
// name: 'terms-of-service',
|
||||
// name: ROUTE_NAMES.TERMS_OF_SERVICE,
|
||||
// component: () => import('@/views/TermsOfServiceView.vue'), // Условия использования
|
||||
// },
|
||||
]
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface MessageDocument extends BaseDocument {
|
||||
type: 'message'
|
||||
|
||||
/** Идентификатор QR‑кода, к которому относится сообщение */
|
||||
qr_code: string
|
||||
qr_code_uri: string
|
||||
|
||||
/** Идентификатор чата для связывания сообщений */
|
||||
chat: string
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { BaseDocument } from './BaseDocument'
|
||||
|
||||
export type ContactInfo = {
|
||||
name?: string
|
||||
phone?: string
|
||||
email?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Интерфейс для документа QR‑кода.
|
||||
* Представляет QR‑код, созданный пользователем, и информацию о привязанном объекте.
|
||||
@@ -30,4 +36,6 @@ export interface QRCodeDocument extends BaseDocument {
|
||||
|
||||
/** Предустановленное сообщение для гостей */
|
||||
predefinedMessages?: string[]
|
||||
|
||||
contactInfo?: ContactInfo
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { createQrCodeDocument } from '@/api/qrCode'
|
||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
||||
import { ROUTE_NAMES } from '@/constants/routes'
|
||||
|
||||
// Состояния шагов
|
||||
const placement = ref<null | QRCodeDocument['placement']>(null)
|
||||
@@ -70,7 +71,10 @@ async function createQrCode() {
|
||||
})
|
||||
|
||||
error.value = null
|
||||
await router.push({ name: 'ManageQRCodeReady', params: { qr_code_uri: qrCodeDocument.uri } })
|
||||
await router.push({
|
||||
name: ROUTE_NAMES.MANAGE_QR_CODE_READY,
|
||||
params: { qr_code_uri: qrCodeDocument.uri },
|
||||
})
|
||||
} catch (err) {
|
||||
error.value = (err as Error).message || 'Неизвестная ошибка'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="flex flex-col min-h-screen">
|
||||
<!-- Основной контент -->
|
||||
<main class="flex-grow flex flex-col">
|
||||
<section class="flex flex-col flex-grow max-w-prose w-full mx-auto p-4">
|
||||
<ChatComponent userRole="guest" :qr_code_uri="qr_code_uri" :chat="chat">
|
||||
<template #endOfMessages="{ count }">
|
||||
<!-- Уведомление, если гость отправил только одно сообщение -->
|
||||
<div
|
||||
v-if="count === 1"
|
||||
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
|
||||
>
|
||||
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
|
||||
<p class="text-gray-600 mt-2">
|
||||
Вы можете написать ещё одно сообщение или подождать ответа.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="mt-10 text-sm text-gray-500 text-center">
|
||||
Хотите создать собственный QR‑код?
|
||||
<a href="/create" class="underline">Создайте его здесь</a>.
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-gray-500 text-center">
|
||||
Сделайте НаСвязи удобнее. Мы будем рады вашей <RouterLink
|
||||
to="/donate"
|
||||
class="underline"
|
||||
>поддержке</RouterLink
|
||||
>.
|
||||
</p>
|
||||
</template>
|
||||
</ChatComponent>
|
||||
</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="/donate" class="underline"-->
|
||||
<!-- >поддержке</a-->
|
||||
<!-- >.-->
|
||||
<!-- </p>-->
|
||||
<!-- </footer>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { MessageDocument } from '@/types/DBDocumentTypes'
|
||||
import ChatComponent from '@/components/ChatComponent.vue'
|
||||
defineProps<{ qr_code_uri: MessageDocument['qr_code_uri']; chat: MessageDocument['chat'] }>()
|
||||
</script>
|
||||
@@ -2,14 +2,15 @@
|
||||
<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-prose w-full flex flex-col gap-10">
|
||||
<template v-for="action in actions">
|
||||
<ReadContactInfo v-if="action === 'viewContactInfo'" :contactInfo="contactInfo" />
|
||||
<ReadMessageSender
|
||||
v-else-if="action === 'sendMessage'"
|
||||
:qr_code_uri="qr_code_uri"
|
||||
:predefinedMessages="predefinedMessages"
|
||||
/>
|
||||
</template>
|
||||
<QueryRender :query #default="{ data: doc }">
|
||||
<template v-for="action in doc.actions" :key="action">
|
||||
<ReadContactInfo
|
||||
v-if="action === 'viewContactInfo' && doc.contactInfo"
|
||||
:contactInfo="doc.contactInfo"
|
||||
/>
|
||||
<ReadMessageSender v-else-if="action === 'sendMessage'" :doc />
|
||||
</template>
|
||||
</QueryRender>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
@@ -28,24 +29,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { getQrCodeDocument } from '@/api/qrCode'
|
||||
import type { QRCodeDocument } from '@/types/DBDocumentTypes'
|
||||
import QueryRender from '@/components/QueryRender.vue'
|
||||
|
||||
const { qr_code_uri } = defineProps<{ qr_code_uri: string }>()
|
||||
|
||||
// Локальные состояния
|
||||
const predefinedMessages = ref<string[]>([])
|
||||
const contactInfo = ref<{ name?: string; phone?: string; email?: string }>({})
|
||||
const actions = ref<string[]>([])
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const qrCodeDoc = await getQrCodeDocument(qr_code_uri)
|
||||
predefinedMessages.value = qrCodeDoc!.predefinedMessage || []
|
||||
contactInfo.value = qrCodeDoc!.contactInfo || { name: 'waka', phone: '+123' }
|
||||
actions.value = qrCodeDoc!.actions || []
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
// Хук для загрузки данных QR-кода
|
||||
const query = useQuery({
|
||||
queryKey: ['qrCodeData', qr_code_uri],
|
||||
queryFn: (): Promise<QRCodeDocument> => getQrCodeDocument(qr_code_uri),
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user