Соединение с портлетом |
---|
url | http://youtube.com/watch?v=ltiqwSGoqhU |
---|
|
Работа с экраном приветствия
Шаг 1.
Реализуйте получение информации о пользователях при монтировании компонента с помощью метода onInfo()
модуля faceRecognizeService
. Для этого добавьте следующий текст в mounted()
в defineComponent
компонента Home
.
Блок кода |
---|
|
this.$robot.faceRecognizeService.onInfo(info => {
}) |
Шаг 2.
Реализуйте метод getCount()
для подсчёта лиц в кадре. Для этого добавьте следующий текст в methods
в defineComponent
компонента Home
.
Блок кода |
---|
|
getCount (info: FaceStructure[]): number {
return info.filter(user => user.id > FACE_RECOGNIZING_CODE).length
} |
Метод возвращает количество элементов массива, где ID
пользователя больше, чем -1
.
FACE_RECOGNIZING_CODE
— это константа, в которой хранится значение -1
, оно означает состояние, когда пользователь появился в кадре, но еще не определен.
Шаг 3.
Добавьте константу, где будет храниться количество лиц в кадре. Вызовите метод getCount()
и сохраните полученное значение в этой константе. Для этого добавьте следующий текст в реализацию метода onInfo()
в defineComponent
компонента Home
.
Блок кода |
---|
|
const count = this.getCount(info) |
Шаг 4.
Добавьте переменную для хранения текущего количества лиц. Для этого добавьте следующий текст в defineComponent
компонента Home
.
Блок кода |
---|
|
data () {
return {
count: 0
}
}, |
Шаг 5.
Добавьте обновление переменной текущего количества лиц, если оно отличается от константы. Для этого добавьте следующий текст в реализацию метода onInfo()
в defineComponent
компонента Home
.
Блок кода |
---|
|
if (count !== this.count) {
this.count = count
} |
Шаг 6.
Реализуйте метод getGeneralUser()
для определения главного лица в кадре. Для этого добавьте следующий текст в methods
в defineComponent
компонента Home
.
Блок кода |
---|
|
getGeneralUser (info: FaceStructure[]): FaceStructure | undefined {
return info.find(user => user.id > FACE_RECOGNIZING_CODE && user.is_tracking)
} |
Метод возвращает объект FaceStructure
, либо undefined
.
Шаг 7.
Создайте файл user.ts
в modules
в store
. Добавьте следующий текст в файл user.ts
. Всё это нам понадобится для определения, знакомый ли пользователь.
Блок кода |
---|
|
import { FaceStructure } from '@pb/api/dist/src/services/face_recognize'
import { Commit } from 'vuex'
export const MODULE_NAME = 'user'
interface UserStore {
user: FaceStructure | undefined
}
export const ACTIONS = {
SET_FACE_INFO: 'SET_FACE_INFO'
}
export const MUTATIONS = {
SET_FACE_INFO: 'SET_FACE_INFO'
}
export const GETTERS = {
GET_USER: 'GET_USER'
}
export const user = {
namespaced: true,
state: (): UserStore => ({
user: undefined
}),
actions: {
[ACTIONS.SET_FACE_INFO]: ({ commit }: { commit: Commit }, payload?: FaceStructure): void => commit(MUTATIONS.SET_FACE_INFO, payload)
},
mutations: {
[MUTATIONS.SET_FACE_INFO]: (state: UserStore, payload?: FaceStructure): void => {
state.user = payload
}
},
getters: {
[GETTERS.GET_USER]: (status: UserStore): FaceStructure | undefined => status.user
}
} |
Шаг 8.
Добавьте computed
-свойство, которое будет возвращать user
из store
. Для этого добавьте следующий текст в defineComponent
компонента Home
.
Блок кода |
---|
|
computed: {
...mapGetters(USER, {
person: USER_GETTERS.GET_USER
})
} |
Шаг 9.
Добавьте константу, где будет храниться главное лицо в кадре (объект FaceStructure
). Вызовите метод getGeneralUser()
и сохраните полученное значение в этой константе. Для этого добавьте следующий текст в реализацию метода onInfo()
в defineComponent
компонента Home
.
Блок кода |
---|
|
const general = this.getGeneralUser(info) |
Шаг 10.
Добавьте проверку главного лица в кадре. Для этого добавьте следующий текст в реализацию метода onInfo()
в defineComponent
компонента Home
.
Блок кода |
---|
|
if (general?.id !== this.person.id) {
} |
Шаг 11.
Добавьте реализацию обновления главного лица в кадре. Для этого добавьте следующий текст в methods
в defineComponent
компонента Home
.
Блок кода |
---|
|
...mapActions(USER, {
setFaceInfo: USER_ACTIONS.SET_FACE_INFO
}), |
Шаг 12.
Добавьте открытие видеопотока при ненулевом количестве лиц. Для этого добавьте следующий текст в реализацию метода onInfo()
в defineComponent
компонента Home
.
Блок кода |
---|
|
if (this.count !== 1) {
this.$router.push('/video')
} |
Шаг 13.
Добавьте путь до компонента видеопотока в router\index.ts
. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>
.
Блок кода |
---|
|
{
path: '/video',
name: 'Video',
component: () => import('../views/VideoStream.vue')
}, |
Шаг 14.
Добавьте проверку, знакомый ли пользователь (для случая, когда количество лиц = 1). Добавьте файл checkPerson.ts
в папку helpers
. Реализуйте метод checkPerson()
. Метод будет перенаправлять знакомого пользователя на страницу главного меню, а незнакомого пользователя — на страницу знакомства. Для этого добавьте следующий текст в файл checkPerson.ts
.
Блок кода |
---|
|
import store from '@/store'
import { GETTERS as USER_GETTERS, MODULE_NAME as USER } from '@/store/modules/user'
import router from '@/router'
export function checkPerson () {
const personFromStorageId = store.getters[`${USER}/${USER_GETTERS.GET_USER}`]?.id ?? ''
const personFromLocalStorage = JSON.parse(localStorage.getItem(personFromStorageId) || 'null')
personFromLocalStorage == null
? router.push('/acquaintance')
: router.push('/menu')
} |
Шаг 15.
Добавьте вызов метода checkPerson()
в реализацию метода onInfo()
в defineComponent
компонента Home
. (строка 19)
Блок кода |
---|
language | js |
---|
linenumbers | true |
---|
collapse | true |
---|
|
mounted () {
this.$robot.faceRecognizeService.onInfo(info => {
const count = this.getCount(info)
if (count !== this.count) {
this.count = count
}
const general = this.getGeneralUser(info)
if (general?.id !== this.person.id) {
this.setFaceInfo(general)
}
if (this.count !== 1) {
this.$router.push('/video')
}
checkPerson()
})
}, |
Шаг 16.
Добавьте путь до страниц главного меню и знакомства в router\index.ts
. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>
.
Блок кода |
---|
|
{
path: '/acquaintance',
name: 'Acquaintance',
component: () => import('../views/AcquaintanceForm.vue')
},
{
path: '/menu',
name: 'MainMenu',
component: () => import('../views/MainMenu.vue')
}, |
Работа с экраном видеопотока
Шаг 17.
Добавьте файл VideoStream.vue
в папку views
. Это экран видеопотока, на котором будет картинка с видеопотоком (получаемым в src
) и кнопка Продолжить. Добавьте следующий текст в этот файл.
Блок кода |
---|
|
<template>
<section class="face-request">
<img
id="video-stream"
class="video-stream"
src="http://localhost:8070/stream?topic=/facerecognition/image_raw&quality=60"
alt="Видеопоток"
width="768"
height="432"
@click.left="handleClick"
/>
<pb-button
class="_md"
:disabled="disabled"
@click="moveNext"
>
Продолжить
</pb-button>
</section>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'VideoStream'
})
</script>
<style lang="scss" scoped>
.face-request {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
padding-bottom: 20px;
& button {
margin-top: 30px;
}
}
</style> |
Шаг 18.
Создайте тип UnsubscribeFunction
. Для этого добавьте следующий текст внутри тегов <script></script>
файла VideoStream.vue
.
Блок кода |
---|
|
type UnsubscribeFunction = () => boolean |
Шаг 19.
Создайте интерфейс Data
в файле VideoStream.vue
. Для этого добавьте следующий текст внутри тегов <script></script>
файла VideoStream.vue
.
Блок кода |
---|
|
interface Data {
faceEventSubscriber?: UnsubscribeFunction,
disabled: boolean
} |
Шаг 20.
Реализуйте функцию-подписчик FaceEventSubscriber
. Для этого добавьте следующий текст в data()
в defineComponent
компонента VideoStream
.
Блок кода |
---|
|
data (): Data {
return {
faceEventSubscriber: undefined,
disabled: true
}
}, |
Шаг 21.
Реализуйте монтирование компонента. Это нужно для подписки на события от робота, которые будут сигнализировать о том, что установлен главный пользователь. Когда установлен главный пользователь, разблокируется кнопка Продолжить (disabled
станет false
). Для этого добавьте следующий текст в defineComponent
компонента VideoStream
.
Блок кода |
---|
|
mounted () {
this.faceEventSubscriber = this.$robot.faceRecognizeService.onGeneralSetted(() => {
this.disabled = false
})
} |
Шаг 22.
Реализуйте обработку кликов по видеопотоку. В этом обработчике вызовите метод Promobot API faceRecognizeService()
для установки главного пользователя. Для этого добавьте следующий текст в methods
в defineComponent
компонента VideoStream
.
Блок кода |
---|
|
handleClick: function (event: MouseEvent & { target: HTMLImageElement}) {
if (event.target) {
const coordinates = {
x: event.offsetX / event.target.clientWidth,
y: event.offsetY / event.target.clientHeight
}
this.$robot.faceRecognizeService.touchGeneral(coordinates)
}
} |
Шаг 23.
Реализуйте обработку кликов по кнопке Продолжить. При нажатии кнопки должна происходить проверка пользователя (знакомый или незнакомый) и в зависимости от этого, перенаправление на страницу главного меню или страницу знакомства. Метод для такой проверки уже реализован ранее, это checkPerson()
.Вызовите этот метод в обработчике кнопки. Для этого добавьте следующий текст в methods
в defineComponent
компонента VideoStream
.
Блок кода |
---|
|
moveNext (): void {
checkPerson()
} |
Шаг 24.
Реализуйте метод beforeUnmount()
. При размонтировании компонента нужно очистить src
картинки и вызвать метод faceEventSubscriber()
. Для этого добавьте следующий текст в methods
в defineComponent
компонента VideoStream
.
Блок кода |
---|
|
beforeUnmount () {
const image = document.getElementById('video-stream') as HTMLImageElement
image.src = ''
this.faceEventSubscriber?.()
} |
Работа с экраном заполнения данных
Шаг 25.
Добавьте страницу AcquaintanceForm.vue
в папку views
. Это экран, на котором есть поле для ввода имени и кнопка сохранения. В этом проекте использована наша библиотека компонентов Promobot Library
, но можно использовать любую другую. Добавьте следующий текст в этот файл.
Блок кода |
---|
|
<template>
<Header title="Как вас зовут?"/>
<section class="acquaintance">
<pb-input
class="acquaintance__input"
v-model="name"
:input-options="{
label: 'Ваше имя',
placeholder: 'Введите имя',
enableErase: true
}"
/>
<pb-button
class="_lg"
:disabled="!isNotEmptyName"
@click="saveUser"
>
Продолжить
</pb-button>
</section>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Header from '@/components/Header.vue'
import { AcquaintanceCore, AcquaintanceProcessSteps } from '@/helpers/AcquintanceCore'
import { mapActions, mapGetters } from 'vuex'
import { ACTIONS as USER_ACTIONS, GETTERS as USER_GETTERS, MODULE_NAME as USER } from '@/store/modules/user'
export default defineComponent({
name: 'AcquaintanceForm',
components: {
Header
}
})
</script>
<style lang="scss" scoped>
.acquaintance {
display: flex;
flex-direction: column;
align-items: center;
padding: 30px;
&__input {
width: 720px;
margin-bottom: 60px;
}
}
</style> |
Шаг 26.
Реализуйте метод сохранения пользователя в базу данных и в localStorage
. Реализуйте его в data()
с помощью Promobot API
, создав instance
ядра знакомства AcquaintanceCore
. Для этого добавьте следующий текст в defineComponent
компонента AcquaintanceForm
.
Блок кода |
---|
|
data () {
return {
name: '',
core: new AcquaintanceCore(this.$robot)
}
} |
Шаг 27.
Добавьте computed-свойство, которое будет возвращать user
из store
(аналогично шагу 8). Для этого добавьте следующий текст в defineComponent
компонента AcquaintanceForm
.
Блок кода |
---|
|
computed: {
...mapGetters(USER, {
person: USER_GETTERS.GET_USER
})
} |
Шаг 28.
Реализуйте проверку на заполненность поля name
. Для этого добавьте следующий текст в computed
в defineComponent
компонента AcquaintanceForm
.
Блок кода |
---|
|
isNotEmptyName (): boolean {
return Boolean(this.name.length)
} |
Шаг 29.
Реализуйте метод сохранения пользователя и обновления store
новыми данными.
В методе в первую очередь происходит проверка на наличие person и на валидность ID
. Далее из функции startAcquaintance()
происходит получение нового ID
и шага. Если шаг "локальное знакомство", т.е. удалось познакомиться, но запись не добавлена в глобальную базу, то вызывается метод ядра delete
(т.е. delLocalUser
) по новому ID
. Иначе происходит обновление store через метод setFaceInfo()
, куда передается текущий person и ID
. В конце происходит сохранение пользователя в localStorage
и переход в главное меню.
Чтобы это сделать добавьте следующий текст в defineComponent
компонента AcquaintanceForm
.
Блок кода |
---|
|
methods: {
...mapActions(USER, {
setFaceInfo: USER_ACTIONS.SET_FACE_INFO
}),
async saveUser (): Promise<void> {
const minValidId = 1000
if (this.person && this.person.id < minValidId) {
const { newId, step } = await this.core.startAcquaintance(this.person, undefined, this.name)
if (step === AcquaintanceProcessSteps.LocalAcquainted) {
this.core.delLocalUser(newId)
} else {
this.setFaceInfo({ ...this.person, id: newId })
}
}
localStorage.setItem(`${this.person?.id}`, JSON.stringify(this.person))
this.$router.push('/menu')
}
} |
Работа с экраном главного меню
Шаг 30.
Добавьте путь до компонента главного меню в router\index.ts
. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>
.
Блок кода |
---|
|
{
path: '/menu',
name: 'MainMenu',
component: () => import('../views/MainMenu.vue')
} |
Шаг 31.
Добавьте страницу MainMenu.vue
в папку views
. Главное меню состоит из двух кнопок: Об отеле и Поболтать. Добавьте следующий текст в этот файл.
Блок кода |
---|
|
<template>
<section class="main-menu">
<button
class="main-menu__button"
@click="aboutHotel"
>
Об отеле
</button>
<button
class="main-menu__button"
@click="toChat"
>
Поболтать
</button>
<button
class="main-menu__button"
@click="toData"
>
Персональные<br>данные
</button>
</section>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'MainMenu',
})
</script>
<style lang="scss" scoped>
.main-menu {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
height: 100%;
padding: 100px 20px;
&__button {
font-family: inherit;
font-weight: 600;
font-size: 32px;
line-height: 42px;
background-color: #ffffff;
box-shadow: 0px 10px 15px -8px rgba(29, 29, 47, 0.2);
border: none;
border-radius: 10px;
}
}
</style> |
Шаг 32.
Реализуйте метод для кнопки Об отеле. Для этого добавьте следующий текст в methods
в defineComponent
компонента MainMenu
.
Блок кода |
---|
|
aboutHotel (): void {
this.$robot.dialogService.sayReplicByName('ABOUT-HOTEL')
} |
Шаг 33.
Реализуйте метод для кнопки Поболтать. Для этого добавьте следующий текст в methods
в defineComponent
компонента MainMenu
.
Блок кода |
---|
|
toChat (): void {
this.$router.push('/chat')
} |
Шаг 34.
Добавьте путь до компонента кнопки Поболтать в router\index.ts
. Для этого добавьте следующий текст в const routes: Array<RouteRecordRaw>
.
Блок кода |
---|
|
{
path: '/chat',
name: 'Chat',
component: () => import('../views/Chat.vue')
} |
Работа с экраном Поболтать
Шаг 35.
Добавьте страницу Chat.vue
в папку views
. Добавьте следующий текст в этот файл.
Блок кода |
---|
|
<template>
<Header show-back-button/>
<section class="chat">
<h2>Задайте ваш вопрос!</h2>
<img src="@/assets/chat-robot.png" width="600" height="320" alt="chat-robot">
</section>
<Footer/>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import Header from '@/components/Header.vue'
import Footer from '@/components/Footer.vue'
export default defineComponent({
name: 'Chat',
components: {
Header,
Footer
}
})
</script>
<style lang="scss" scoped>
.chat {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
height: 565px;
padding: 20px;
& h2 {
margin-bottom: 64px;
font-size: 40px;
font-weight: 400;
line-height: 48px;
}
}
</style> |
Шаг 36.
Добавьте вызов метода, который установит Лингвокейс при монтировании компонента Chat
. Для этого добавьте следующий текст в defineComponent
компонента Chat
.
Блок кода |
---|
|
mounted () {
this.$robot.dialogService.setCase('chat')
} |